Objectos Weekly #004: Type annotations, canonical names and inner classes
Marcio Endo
Posted on December 9, 2022
Welcome to Objectos Weekly issue #004.
I was writing this issue when the power in my apartment went out and I heard a loud noise outside. I looked outside from my balcony and I saw that the tree in front of my building had fallen taking a power line with it. Scary: a live power line was exposed right at the sidewalk. It burned a hole through the pavement. Luckily, there was no one there when it happened.
The power was restored later the same day. But it was already too late to do any meaningful work; I finished this issue the next day.
Type annotations, canonical names and inner classes
I have recently learned a detail regarding type annotations, canonical names and inner classes.
Suppose a class Example
which is a subclass of the Super
type:
package post;
import post.util.Super;
public class Example extends Super {}
Notice that the superclass is in a different package of the subclass. For this reason, a import declaration was required.
Let's annotate the superclass with a Nullable
annotation.
package post;
import post.annotation.Nullable;
import post.util.Super;
public class Example extends @Nullable Super {}
The annotation might indicate to a hypothetical static analysis tool that the superclass fields can hold null
values.
The code above compiles without errors.
Referring to the superclass by its canonical name
What happens if we use the canonical name of the superclass instead? We can remove the import declaration, like so:
package post;
import post.annotation.Nullable;
// does not compile!
public class Example extends @Nullable post.util.Super {}
After this change, the code no longer compiles! javac
issues the following error message:
$ javac -d /tmp src/main/java/post/{,annotation,util}/*.java
src/main/java/post/Example.java:10: error: cannot find symbol
public class Example extends @Nullable post.util.Super {
^
symbol: class post
1 error
It seems javac
was expecting a class right after the Nullable
annotation. Eclipse JDT error message gives more information:
Illegally placed annotation: type annotations must directly precede the simple name of the type they are meant to affect (or the [] for arrays)
OK, let's try to fix it then.
Fixing the compilation error
We must write the annotation right before the simple name of the class, like so:
package post;
import post.annotation.Nullable;
public class Example extends post.util. @Nullable Super {}
Notice how the annotation is after the trailing dot character of the package name.
The code now compiles without errors:
$ javac -d /tmp src/main/java/post/{,annotation,util}/*.java
[no errors emitted]
Well, I didn't know about that.
Why though?
I don't know for sure. But I think inner classes are one of the reasons.
In the previous issue I talked about inner classes and qualified superclass constructor invocations. The main takeaway to remember is:
- in order to have an instance of the inner class we must provide an instance of the outer class.
Extending an inner class of Super
Let's say our superclass Super
declares an inner class called Inner
.
Let's have our Example
class extend Inner
instead of Super
:
package post;
public class Example extends post.util.Super.Inner {
public Example(post.util.Super outer) {
outer.super();
}
}
As Inner
is an inner class, we must provide an instance of Super
. That is why we had to add the constructor and use the qualified superclass constructor invocation.
Let's get back to our hypothetical static analysis tool. Let's say we would like to:
- allow
null
values to theSuper
fields; but - disallow
null
values to theInner
fields.
We can annotate our class like so:
import post.annotation.NotNull;
import post.annotation.Nullable;
public class Example extends post.util.@Nullable Super.@NotNull Inner {
public Example(post.util.Super outer) {
outer.super();
}
}
That's peculiar. I can definitely say I don't see it everyday.
It compiles without errors:
$ javac -d /tmp src/main/java/post/{,annotation,util}/*.java
[no errors emitted]
Objectos Code Weekly
Work on Objectos Code is still in progress. Please note that the syntax is still subject to change. In fact I experimented a bit with names that are reserved keywords.
For instance, to write a package declaration in Objectos you write:
_package("com.example.model");
As package
is a reserved keyword, the name of the method has a leading underscore character. I tried the following variations:
packageDeclaration("com.example.model");
packageDecl("com.example.model");
package_("com.example.model");
package$("com.example.model");
But I was not pleased with any of them. So I decided to stick with the leading underscore version.
Method type parameters and type variables
You can declare a method with type parameters. The following Objectos Code:
public class TypeParams extends JavaTemplate {
@Override
protected final void definition() {
_package("objectos.example");
autoImports();
_class(
_public(), _final(), id("Util"),
constructor(_private()),
method(
_public(), _static(),
tparam("T", t("com.example", "Foo")),
_void(),
id("sort"),
param(t(t("java.util", "List"), tvar("T")), id("list")),
invoke(t(Collections.class), "sort", n("list"))
)
);
}
}
Generates:
package objectos.example;
import com.example.Foo;
import java.util.Collections;
import java.util.List;
public final class Util {
private Util() {}
public static <T extends Foo> void sort(List<T> list) {
Collections.sort(list);
}
}
Parameterized types
You might have noticed in the previous example that you can write type arguments of a generic type. The following Objectos Code:
public class ParameterizedTypes extends JavaTemplate {
@Override
protected final void definition() {
_package("objectos.example");
autoImports();
_class(
_public(), id("ParameterizedTypesExample"),
field(
t(t("java.util", "Map"), t(String.class), t("java.lang", "Integer")),
id("map")
),
field(t(t(Set.class), t(OpenOption.class)), id("set")),
field(
t(
t("java.util", "List"),
t(t(Map.class), t(String.class), t(String.class))
), id("list")
)
);
}
}
Generates:
package objectos.example;
import java.nio.file.OpenOption;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ParameterizedTypesExample {
Map<String, Integer> map;
Set<OpenOption> set;
List<Map<String, String>> list;
}
Until the next issue of Objectos Weekly
So that's it for today. I hope you've enjoyed reading.
The source code of all of the examples are in this GitHub repository.
If you liked this content please consider subscribing to the blog via email.
Posted on December 9, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
December 17, 2022