Weird behavior with FQ type names - javaparser

I'm trying to extract all class/interface type references from a class declarations. I'm getting a weird behavior with FQ names.
Here is a simple example:
CompilationUnit cu = StaticJavaParser.parse(
"public class foo extends java.lang.String {}");
cu.stream().
filter(ClassOrInterfaceType.class::isInstance).
forEach(System.out::println);
the output is:
java.lang.String
java.lang
java
I was expecting the java.lang.String but the others? What does it ever mean that java.lang is a ClassOrInterfaceType?
I'm wondering if this is the expected behavior, in any case: is there a way to filter out these spurious elements? Thanks.

You can do it recursively
String s = "class Foo extends java.lang.String {}";
CompilationUnit cu = StaticJavaParser.parse(s);
cu.findAll(ClassOrInterfaceDeclaration.class).forEach(cid -> {
System.out.println(cid.getNameAsString());
cid.getExtendedTypes().forEach(extType -> System.out.println(extType.getNameAsString()));
});

Related

How to properly user Parcelize with generic type in Kotlin?

Can't make the parcelization work as expceted.
That is the class:
#Parcelize
class ResultsWrapper<T>(
#SerializedName("results") var results: T
): Parcelable
In that case it says: Type is not directly supported by 'Parcelize'. Annotate the parameter type with '#RawValue' if you want it to be serialized using 'writeValue()'
Parcelization can't know the T implements Parcelable? No problem. Writing like this:
#Parcelize
class ResultsWrapper<T: Parcelable>(
#SerializedName("results") var results: T
): Parcelable
Now there is no compilation error, but there is build time error:
error: non-static type variable T cannot be referenced from a static context (in newArray and createFromParcel generated functions in static Creator)
Using #RawValue lead to same error.
How to make it right?

What's the difference between KotlinNullPointerException and Java's NullPointerException

As Kotlin doesn't allow for implicit null values/variables, was KotlinNullPointerException introduced to explicitly denote NPE caused by !!? Is that the only purpose of this child class of NullPointerException?
There is no real difference between a KotlinNullPointerException and JavaNullPointerException.
Here is how:
KotlinNullPointerException is an open class that extends NullPointerException Now this NullPointerException is a typealias of java.lang.NullPointerException.
public open class KotlinNullPointerException : NullPointerException {
constructor()
constructor(message: String?) : super(message)
}
This is a line extracted from TypeAlias.Kt
#SinceKotlin("1.1") public actual typealias NullPointerException = java.lang.NullPointerException
Now, if we see the declaration of java.lang.NullPointerException, we are taken to a Java.lang class that extends RuntimeException.
public
class NullPointerException extends RuntimeException {
private static final long serialVersionUID = 5162710183389028792L;
/**
* Constructs a {#code NullPointerException} with no detail message.
*/
public NullPointerException() {
super();
}
/**
* Constructs a {#code NullPointerException} with the specified
* detail message.
*
* #param s the detail message.
*/
public NullPointerException(String s) {
super(s);
}
}
In Kotlin, to make some declaration of nullable type, you have to explicitly allow it by appending ? at the end of declaration type. Example:
var nullableString: String? = null
This is a simple way of saying that this variable could be null anytime, so if you try to access this variable from any part of your code, it will throw error and will force you to take measures to prevent NPE either using !!(crash if null) or ?(skip if null).
It's just a way to make it look more like 'Kotlin'.
As of version 1.3, Kotlin throws KotlinNullPointerException only in case of a failed !! operator check. This distinguishes it from the other cases where NullPointerException can be thrown, for example, when accessing non-initialized members during class construction.
Note, however, that there are plans to remove this distinction in Kotlin 1.4: all such failed checks will throw just NullPointerException, so its inheritor KotlinNullPointerException will become unused. You can read more about that in the announce blog post of 1.3.50 version release: https://blog.jetbrains.com/kotlin/2019/08/kotlin-1-3-50-released/

Whats the interface in angle brackets in Kotlin?

In Kotlin I often read
class MyFragment : BaseMvpFragment<MvpView, MvpPresenter>(), MvpView {}
whereas MvpView and MvpPresenter are interfaces.. so MyFragment extends BaseMvpFragment<MvpView, MvpPresenter>() but how can I interpret <MvpView, MvpPresenter> ?
The class BaseMvpFragment obviously defines two generic types which are being specified via <MvpView, MvpPresenter>.
Consider the List<T> interface. When you implement it, it looks like this:
class VerySpecialList : List<String> { ... }
They are type parameters; see here.

Type inference for parameterized types in case of cyclic Builder scenario

Assuming there exists the following java class:
public class Test {
static class Builder<B extends Builder<B>>{
B asBuilder() {
return (B) this;
}
}
public static <B extends Builder<B>> B newBuilder() {
return new Builder<B>().asBuilder();
}
}
Trying to call Test.newBuilder() in a consuming Kotlin code gives the error Type expected.
Test.newBuilder<>() has the same issue. Test.newBuilder<Test.Builder>() gives the error: One type argument expected for class Builder<B : Test.Builder<B!>!>. Since the type argument is a recursive call this can't be solved in the above fashion.
I believe this is a rather weird behavior even from Java perspective. It's strange that the Test class code was even allowed in its current form. Unfortunately, the above was a simplified version of another class that I have no control of. In reality I am trying to do
org.apache.logging.log4j.core.layout.GelfLayout.newBuilder()

Why can't we have public typealiases from private expanded types?

If I try this:
sealed class Attributes
data class Attributes1(
val prop1: String
) : Attributes()
private data class IMyType<A>(
val attributes: A
) where A: Attributes
typealias MyType1 = IMyType<Attributes1>
...I get the error: 'public' typealias exposes 'private' in expanded type IMyType.
What is/are the reason(s) for preventing this?
Note: Using Kotlin 1.1.4
Edit 1
I understand what typealiases are and I understand the implications of the restrictions in place.
What I'm asking is why those restrictions need to be there.
If you consider my example code... I want MyType1 (and maybe others) to be accessible outside of this file, but I don't want the raw/generic IMyType to be used outside of this file.
Is that not a legitimate use case?
By definition typealias is to provide an alternative name for the existing type. So you can't promoting the visibility for the existing type by typealias.
From the grammar of the typealias, if you want to use typealias in your code, you must make sure the visibility of the typealias is lower or equals than the visibility of the existing type. for example:
internal class Bar
private typealias PrivateFoo = Bar
internal typealias InternalFoo = Bar
//v--- error
public typealias PublicFoo = Bar
The visibility of the type alias should be the same or more restricted as the corresponding type.
When you declare IMyType as private in your file, IMyType is a file-private class which can be accessed only within that kotlin file. If you declare the public type alias MyType1 for IMyType, then it is meaningless for IMyType to be private since MyType1 can be access everywhere. So, it is forbidden.
From Kotlin's doc:
Type aliases do not introduce new types. They are equivalent to the corresponding underlying types. When you add typealias Predicate<T> and use Predicate<Int> in your code, the Kotlin compiler always expand it to (Int) -> Boolean.
The main purpose of having typealias is to provide alternative name for existing types. In your case, both IMyType and MyType1 refer to the same class. You cannot access IMyType through MyType1 outside the file since IMyType intends to be private.
Example to show new type is not introduced:
//Kotlin code
fun acceptMyType1(type: MyType1) {}
//Decompiled code, MyType1 is not used
public static final void acceptMyType1(#NotNull IMyType type) {
Intrinsics.checkParameterIsNotNull(type, "type");
}
Sidetracked problem: You cannot expose a private super class through a public sub class also.