Different generic behaviour when using lambda instead of explicit anonymous inner class - java

The context
I'm working on a project that is heavily dependent on generic types. One of its key components is the so-called TypeToken, which provides a way of representing generic types at runtime and applying some utility functions on them. To avoid Java's Type Erasure, I'm using the curly brackets notation ({}) to create an automatically generated subclass since this makes the type reifiable.
What TypeToken basically does
This is a strongly simplified version of TypeToken which is way more lenient than the original implementation. However, I'm using this approach so I can make sure that the real problem doesn't lie in one of those utility functions.
public class TypeToken<T> {
private final Type type;
private final Class<T> rawType;
private final int hashCode;
/* ==== Constructor ==== */
#SuppressWarnings("unchecked")
protected TypeToken() {
ParameterizedType paramType = (ParameterizedType) this.getClass().getGenericSuperclass();
this.type = paramType.getActualTypeArguments()[0];
// ...
}
When it works
Basically, this implementation works perfectly in almost every situation.
It has no problem with handling most types. The following examples work perfectly:
TypeToken<List<String>> token = new TypeToken<List<String>>() {};
TypeToken<List<? extends CharSequence>> token = new TypeToken<List<? extends CharSequence>>() {};
As it doesn't check the types, the implementation above allows every type that the compiler permits, including TypeVariables.
<T> void test() {
TypeToken<T[]> token = new TypeToken<T[]>() {};
}
In this case, type is a GenericArrayType holding a TypeVariable as its component type. This is perfectly fine.
The weird situation when using lambdas
However, when you initialize a TypeToken inside a lambda expression, things start to change. (The type variable comes from the test function above)
Supplier<TypeToken<T[]>> sup = () -> new TypeToken<T[]>() {};
In this case, type is still a GenericArrayType, but it holds null as its component type.
But if you're creating an anonymous inner class, things start to change again:
Supplier<TypeToken<T[]>> sup = new Supplier<TypeToken<T[]>>() {
#Override
public TypeToken<T[]> get() {
return new TypeToken<T[]>() {};
}
};
In this case, the component type again holds the correct value (TypeVariable)
The resulting questions
What happens to the TypeVariable in the lambda-example? Why does the type inference not respect the generic type?
What is the difference between the explicitly-declared and the implicitly-declared example? Is type inference the only difference?
How can I fix this without using the boilerplate explicit declaration? This becomes especially important in unit testing since I want to check whether the constructor throws exceptions or not.
To clarify it a bit: This is not a problem that's "relevant" for the program since I do NOT allow non-resolvable types at all, but it's still an interesting phenomenon I'd like to understand.
My research
Update 1
Meanwhile, I've done some research on this topic. In the Java Language Specification §15.12.2.2 I've found an expression that might have something to do with it - "pertinent to applicability", mentioning "implicitly typed lambda expression" as an exception. Obviously, it's the incorrect chapter, but the expression is used in other places, including the chapter about type inference.
But to be honest: I haven't really figured out yet what all of those operators like := or Fi0 mean what makes it really hard to understand it in detail. I'd be glad if someone could clarify this a bit and if this might be the explanation of the weird behavior.
Update 2
I've thought of that approach again and came to the conclusion, that even if the compiler would remove the type since it's not "pertinent to applicability", it doesn't justify to set the component type to null instead of the most generous type, Object. I cannot think of a single reason why the language designers decided to do so.
Update 3
I've just retested the same code with the latest version of Java (I used 8u191 before). To my regret, this hasn't changed anything, although Java's type inference has been improved...
Update 4
I've requested an entry in the offical Java Bug Database/Tracker a few days ago and it just got accepted. Since the developers who reviewed my report assigned the priority P4 to the bug, it might take a while until it'll be fixed. You can find the report here.
A huge shoutout to Tom Hawtin - tackline for mentioning that this might be an essential bug in the Java SE itself. However, a report by Mike Strobel would probably be way more detailed than mine due to his impressive background knowledge. However, when I wrote the report, Strobel's answer wasn't yet available.

tldr:
There is a bug in javac that records the wrong enclosing method for lambda-embedded inner classes. As a result, type variables on the actual enclosing method cannot be resolved by those inner classes.
There are arguably two sets of bugs in the java.lang.reflect API implementation:
Some methods are documented as throwing exceptions when nonexistent types are encountered, but they never do. Instead, they allow null references to propagate.
The various Type::toString() overrides currently throw or propagate a NullPointerException when a type cannot be resolved.
The answer has to do with the generic signatures that usually get emitted in class files that make use of generics.
Typically, when you write a class that has one or more generic supertypes, the Java compiler will emit a Signature attribute containing the fully parameterized generic signature(s) of the class's supertype(s). I've written about these before, but the short explanation is this: without them, it would not be possible to consume generic types as generic types unless you happened to have the source code. Due to type erasure, information about type variables gets lost at compilation time. If that information were not included as extra metadata, neither the IDE nor your compiler would know that a type was generic, and you could not use it as such. Nor could the compiler emit the necessary runtime checks to enforce type safety.
javac will emit generic signature metadata for any type or method whose signature contains type variables or a parameterized type, which is why you are able to obtain the original generic supertype information for your anonymous types. For example, the anonymous type created here:
TypeToken<?> token = new TypeToken<List<? extends CharSequence>>() {};
...contains this Signature:
LTypeToken<Ljava/util/List<+Ljava/lang/CharSequence;>;>;
From this, the java.lang.reflection APIs can parse the generic supertype information about your (anonymous) class.
But we already know that this works just fine when the TypeToken is parameterized with concrete types. Let's look at a more relevant example, where its type parameter includes a type variable:
static <F> void test() {
TypeToken sup = new TypeToken<F[]>() {};
}
Here, we get the following signature:
LTypeToken<[TF;>;
Makes sense, right? Now, let's look at how the java.lang.reflect APIs are able to extract generic supertype information from these signatures. If we peer into Class::getGenericSuperclass(), we see that the first thing it does is call getGenericInfo(). If we haven't called into this method before, a ClassRepository gets instantiated:
private ClassRepository getGenericInfo() {
ClassRepository genericInfo = this.genericInfo;
if (genericInfo == null) {
String signature = getGenericSignature0();
if (signature == null) {
genericInfo = ClassRepository.NONE;
} else {
// !!! RELEVANT LINE HERE: !!!
genericInfo = ClassRepository.make(signature, getFactory());
}
this.genericInfo = genericInfo;
}
return (genericInfo != ClassRepository.NONE) ? genericInfo : null;
}
The critical piece here is the call to getFactory(), which expands to:
CoreReflectionFactory.make(this, ClassScope.make(this))
ClassScope is the bit we care about: this provides a resolution scope for type variables. Given a type variable name, the scope gets searched for a matching type variable. If one is not found, the 'outer' or enclosing scope is searched:
public TypeVariable<?> lookup(String name) {
TypeVariable<?>[] tas = getRecvr().getTypeParameters();
for (TypeVariable<?> tv : tas) {
if (tv.getName().equals(name)) {return tv;}
}
return getEnclosingScope().lookup(name);
}
And, finally, the key to it all (from ClassScope):
protected Scope computeEnclosingScope() {
Class<?> receiver = getRecvr();
Method m = receiver.getEnclosingMethod();
if (m != null)
// Receiver is a local or anonymous class enclosed in a method.
return MethodScope.make(m);
// ...
}
If a type variable (e.g., F) is not found on the class itself (e.g., the anonymous TypeToken<F[]>), then the next step is to search the enclosing method. If we look at the disassembled anonymous class, we see this attribute:
EnclosingMethod: LambdaTest.test()V
The presence of this attribute means that computeEnclosingScope will produce a MethodScope for the generic method static <F> void test(). Since test declares the type variable W, we find it when we search the enclosing scope.
So, why doesn't it work inside a lambda?
To answer this, we must understand how lambdas get compiled. The body of the lambda gets moved into a synthetic static method. At the point where we declare our lambda, an invokedynamic instruction gets emitted, which causes a TypeToken implementation class to be generated the first time we hit that instruction.
In this example, the static method generated for the lambda body would look something like this (if decompiled):
private static /* synthetic */ Object lambda$test$0() {
return new LambdaTest$1();
}
...where LambdaTest$1 is your anonymous class. Let's dissassemble that and inspect our attributes:
Signature: LTypeToken<TW;>;
EnclosingMethod: LambdaTest.lambda$test$0()Ljava/lang/Object;
Just like the case where we instantiated an anonymous type outside of a lambda, the signature contains the type variable W. But EnclosingMethod refers to the synthetic method.
The synthetic method lambda$test$0() does not declare type variable W. Moreover, lambda$test$0() is not enclosed by test(), so the declaration of W is not visible inside it. Your anonymous class has a supertype containing a type variable that your the class doesn’t know about because it’s out of scope.
When we call getGenericSuperclass(), the scope hierarchy for LambdaTest$1 does not contain W, so the parser cannot resolve it. Due to how the code is written, this unresolved type variable results in null getting placed in the type parameters of the generic supertype.
Note that, had your lambda had instantiated a type that did not refer to any type variables (e.g., TypeToken<String>) then you would not run into this problem.
Conclusions
(i) There is a bug in javac. The Java Virtual Machine Specification §4.7.7 ("The EnclosingMethod Attribute") states:
It is the responsibility of a Java compiler to ensure that the method identified via the method_index is indeed the closest lexically enclosing method of the class that contains this EnclosingMethod attribute. (emphasis mine)
Currently, javac seems to determine the enclosing method after the lambda rewriter runs its course, and as a result, the EnclosingMethod attribute refers to a method that never even existed in the lexical scope. If EnclosingMethod reported the actual lexically enclosing method, the type variables on that method could be resolved by the lambda-embedded classes, and your code would produce the expected results.
It is arguably also a bug that the signature parser/reifier silently allows a null type argument to be propagated into a ParameterizedType (which, as #tom-hawtin-tackline points out, has ancillary effects like toString() throwing a NPE).
My bug report for the EnclosingMethod issue is now online.
(ii) There are arguably multiple bugs in java.lang.reflect and its supporting APIs.
The method ParameterizedType::getActualTypeArguments() is documented as throwing a TypeNotPresentException when "any of the actual type arguments refers to a non-existent type declaration". That description arguably covers the case where a type variable is not in scope. GenericArrayType::getGenericComponentType() should throw a similar exception when "the underlying array type's type refers to a non-existent type declaration". Currently, neither appears to throw a TypeNotPresentException under any circumstances.
I would also argue that the various Type::toString overrides should merely fill in the canonical name of any unresolved types rather than throwing a NPE or any other exception.
I have submitted a bug report for these reflection-related issues, and I will post the link once it is publicly visible.
Workarounds?
If you need to be able to reference a type variable declared by the enclosing method, then you can't do that with a lambda; you'll have to fall back to the longer anonymous type syntax. However, the lambda version should work in most other cases. You should even be able to reference type variables declared by the enclosing class. For example, these should always work:
class Test<X> {
void test() {
Supplier<TypeToken<X>> s1 = () -> new TypeToken<X>() {};
Supplier<TypeToken<String>> s2 = () -> new TypeToken<String>() {};
Supplier<TypeToken<List<String>>> s3 = () -> new TypeToken<List<String>>() {};
}
}
Unfortunately, given that this bug has apparently existed since lambdas were first introduced, and it has not been fixed in the most recent LTS release, you may have to assume the bug remains in your clients’ JDKs long after it gets fixed, assuming it gets fixed at all.

As a workaround, you can move the creation of TypeToken out of lambda to a separate method, and still use lambda instead of fully declared class:
static<T> TypeToken<T[]> createTypeToken() {
return new TypeToken<T[]>() {};
}
Supplier<TypeToken<T[]>> sup = () -> createTypeToken();

I've not found the relevant part of the spec, but here's a partial answer.
There's certainly a bug with the component type being null. To be clear, this is TypeToken.type from above cast to GenericArrayType (yuck!) with the method getGenericComponentType invoked. The API docs do not explicitly mention whether the null returned is valid or not. However, the toString method throws NullPointerException, so there is definitely a bug (at least in the random version of Java I am using).
I don't have a bugs.java.com account, so can't report this. Someone should.
Let's have a look at the class files generated.
javap -private YourClass
This should produce a listing containing something like:
static <T> void test();
private static TypeToken lambda$test$0();
Notice that our explicit test method has it's type parameter, but the synthetic lambda method does not. You might expect something like:
static <T> void test();
private static <T> TypeToken<T[]> lambda$test$0(); /*** DOES NOT HAPPEN ***/
// ^ name copied from `test`
// ^^^ `Object[]` would not make sense
Why doesn't this happen. Presumably because this would be a method type parameter in a context where a type type parameter is required, and they are surprisingly different things. There is also a restriction on lambdas not allowing them to have method type parameters, apparently because there is no explicit notation (some people may suggest this seems like a poor excuse).
Conclusion: There is at least one unreported JDK bug here. The reflect API and this lambda+generics part of the language is not to my taste.

Related

Generic Erasure concept

Could you please help me to understand the generic concept here.
// Can't create an instance of T.
class Gen<T> {
T ob;
Gen() {
ob = new T(); // Illegal!!!
}
public static void main() {
Gen<Integer> genobj = new Gen<Integer>(); //Error
}
}
When your Java code is compiled, all generic type
information is removed (erased). This means replacing type parameters with their bound
type, which is Object if no explicit bound is specified, and then applying the appropriate
casts (as determined by the type arguments) to maintain type compatibility with the types
specified by the type arguments. The compiler also enforces this type compatibility.
My question:-Why java complier is throwing error here ?
Bevause after complitaion .
Thanks
There are a few ways that may work out here:
From a logical POV:
It's not even guaranteed that whatever template-parameter T you use has a default-constructor. Which obviously offers the problem of how to handle the absence of a default-constructor. Possible solutions would be to produce a runtime-error, compile-time error or disallow any T that doesn't provide a default-constructor. The latter would obviously break the template-definition, which allows any T. And the runtime-error would complicate things quite a bit and yield the same problem as mentioned above. Remains preventing this behavior in the first place and throwing a compile-time error.
From a internal view:
Let's assume we could use the provided code. Then how would it work? Due to erasure, new T() would produce an Object. But what if T is Integer? Well, we're screwed. An Object is not an Integer, so we'll get a plain class-cast exception.
So in summary: allowing the above to compile wouldn't work from a practical POV and in addition break the current definition of generics in java.

Why is this overloading wrong? [duplicate]

Why is it not legal to have the following two methods in the same class?
class Test{
void add(Set<Integer> ii){}
void add(Set<String> ss){}
}
I get the compilation error
Method add(Set) has the same erasure add(Set) as another method in type Test.
while I can work around it, I was wondering why javac doesn't like this.
I can see that in many cases, the logic of those two methods would be very similar and could be replaced by a single
public void add(Set<?> set){}
method, but this is not always the case.
This is extra annoying if you want to have two constructors that takes those arguments because then you can't just change the name of one of the constructors.
This rule is intended to avoid conflicts in legacy code that still uses raw types.
Here's an illustration of why this was not allowed, drawn from the JLS. Suppose, before generics were introduced to Java, I wrote some code like this:
class CollectionConverter {
List toList(Collection c) {...}
}
You extend my class, like this:
class Overrider extends CollectionConverter{
List toList(Collection c) {...}
}
After the introduction of generics, I decided to update my library.
class CollectionConverter {
<T> List<T> toList(Collection<T> c) {...}
}
You aren't ready to make any updates, so you leave your Overrider class alone. In order to correctly override the toList() method, the language designers decided that a raw type was "override-equivalent" to any generified type. This means that although your method signature is no longer formally equal to my superclass' signature, your method still overrides.
Now, time passes and you decide you are ready to update your class. But you screw up a little, and instead of editing the existing, raw toList() method, you add a new method like this:
class Overrider extends CollectionConverter {
#Override
List toList(Collection c) {...}
#Override
<T> List<T> toList(Collection<T> c) {...}
}
Because of the override equivalence of raw types, both methods are in a valid form to override the toList(Collection<T>) method. But of course, the compiler needs to resolve a single method. To eliminate this ambiguity, classes are not allowed to have multiple methods that are override-equivalent—that is, multiple methods with the same parameter types after erasure.
The key is that this is a language rule designed to maintain compatibility with old code using raw types. It is not a limitation required by the erasure of type parameters; because method resolution occurs at compile-time, adding generic types to the method identifier would have been sufficient.
Java generics uses type erasure. The bit in the angle brackets (<Integer> and <String>) gets removed, so you'd end up with two methods that have an identical signature (the add(Set) you see in the error). That's not allowed because the runtime wouldn't know which to use for each case.
If Java ever gets reified generics, then you could do this, but that's probably unlikely now.
This is because Java Generics are implemented with Type Erasure.
Your methods would be translated, at compile time, to something like:
Method resolution occurs at compile time and doesn't consider type parameters. (see erickson's answer)
void add(Set ii);
void add(Set ss);
Both methods have the same signature without the type parameters, hence the error.
The problem is that Set<Integer> and Set<String> are actually treated as a Set from the JVM. Selecting a type for the Set (String or Integer in your case) is only syntactic sugar used by the compiler. The JVM can't distinguish between Set<String> and Set<Integer>.
Define a single Method without type like void add(Set ii){}
You can mention the type while calling the method based on your choice. It will work for any type of set.
It could be possible that the compiler translates Set(Integer) to Set(Object) in java byte code. If this is the case, Set(Integer) would be used only at compile phase for syntax checking.
I bumped into this when tried to write something like:
Continuable<T> callAsync(Callable<T> code) {....}
and
Continuable<Continuable<T>> callAsync(Callable<Continuable<T>> veryAsyncCode) {...}
They become for compiler the 2 definitions of
Continuable<> callAsync(Callable<> veryAsyncCode) {...}
The type erasure literally means erasing of type arguments information from generics.
This is VERY annoying, but this is a limitation that will be with Java for while.
For constructors case not much can be done, 2 new subclasses specialized with different parameters in constructor for example.
Or use initialization methods instead... (virtual constructors?) with different names...
for similar operation methods renaming would help, like
class Test{
void addIntegers(Set<Integer> ii){}
void addStrings(Set<String> ss){}
}
Or with some more descriptive names, self-documenting for oyu cases, like addNames and addIndexes or such.
In this case can use this structure:
class Test{
void add(Integer ... ii){}
void add(String ... ss){}
}
and inside methods can create target collections
void add(Integer ... values){
this.values = Arrays.asList(values);
}

If the signature of a constructor includes the definition of a generic type, what extra responsibilities does that put on users of that constructor?

I extending an existing project that use generics in a few classes.
I am working on a class called PhiFunction, this class has one construcor which accepts a number of arguments. These include two arguments which I want to force to be the same type, E. These arguments are only used in the constructor, they are not stored as class fields.
I dont want to add this type to the class signature (I want to keep the class definition as simple as possible). The class signature just includes the type T at the moment. To keep the class signature as simple as possible, I added the definition of this type to the constructor signature, as shown below:
Simplified old code:
public class PhiFunction<T> {
...
public PhiFunction(
final MathematicalGroup<?> group, final List<?> baseElements, ...) {
Simplified new code:
public class PhiFunction<T> {
...
public <E extends GroupElement<T>> PhiFunction(
final MathematicalGroup<E> group, final List<E> baseElements, ...) {
The code compiles fine, and works fine.
The thing which surprises and confuses me now is that, it is still possible to create instances of PhiFunction as before. In other words, the addition of this type definition in the constructor signature does not change how the constructor is used (assuming that users do indeed supply arguments which use the same type). Users can still create instances of PhiFunction, just as they did before, without caring that this type definition was added to the constructor. I expected that users of this constructor would have to define E as being some particular class, but they dont.
I had not used generics in this way before. It seems that adding a type definition to the constructor (or to any method I guess) simply allows a type to be defined, without putting responsibilities on the user of the constructor to define that type.
I guess my question is, if the signature of a constructor includes the specification of a generic type, what extra responsibilities does that put on the user of the constructor?
I have one other related question. Should this type E be added to the Java docs as a parameter? and how? I am sure that it should be documented, as two of the arguments of the constructor must be of that type, but I am not sure how this should be added to the Javadocs.
You don't need to do any extra work for invoking a generic method or constructor. Most of the time, the compiler will be able to infer the type argument, based on the argument you pass to the method, or from the return type (not applicable here though).
So, when you create an instance of that class like this:
MathematicalGroup<Sometype> mathematicalGroup;
List<Sometype> list;
PhiFunction<Double> phiFunction = new PhiFunction<>(mathematicalGroup, list);
...the type parameter E of the constructor will automatically be inferred as Sometype.
However, if you pass arguments that doesn't comply to the rules of type parmeters, you'll see a compiler error, as in this case:
MathematicalGroup<Sometype> mathematicalGroup;
List<SomeOthertype> list;
// This will give a compiler error.
PhiFunction<Double> phiFunction = new PhiFunction<>(mathematicalGroup, list);
You also have the option to give explicit type arguments, if sometimes type inference doesn't work as expected:
// Redundant usage of explicit type argument
PhiFunction<Double> phiFunction =
new <Sometype>PhiFunction<Double>(mathematicalGroup, list);
Although the above usage of explicit type argument is redundant, but there are cases where compiler will not infer the type that you expect it to. Those are the cases when you pass inconsistent arguments to the parameters that are of same type parameter. Consider the below method for example:
public static <T> void fill( T [] array, T elem) {
for (int i=0; i<array.length; ++i) { array[i] = elem; }
}
.. if you try to invoke that method as:
fill(new String [5], new String ("XYZ")); // This is fine
fill(new String [5], new Integer (100)); // This is also fine? How?
Ideally you would expect the second method invocation to fail, because String and Integer should not be substitutable for same type parameter T. But here's the surprise. The compiler infers the type parameter as the intersection of all the super types of the arguments you passed. So, the type T is inferred as:
T:=Object&Serializable&Comparable
In that case, you might want to give <Object> as explicit type argument:
YourClass.<Object>fill(new String[5], new Integer(100));
P.S: Did you know that you can invoke non-generic methods in generic way? Well, generics is full of surprises :)
As for Javadoc, no you don't need to give any information about what E represent. Just explain what the paramters group and list means. That's it. The type parameter and formal parameters are anyways the part of the method signature, and will already be there. Consider for example Arrays.binarySearch() method.
To add it to Javadoc:
#param <E> This represents...
This constructor
public PhiFunction(
final MathematicalGroup<?> group, final List<?> baseElements, ...) {
Allows any type to be passed to/used with MathematicalGroup. Does MathematicalGroup have its own generics associated to it already?
If so, then perhaps your new constructor is redundant. That is, even though the original constructor has <?>, the generics restrictions of the MathematicalGroup class itself already does this enforcement.
public <E extends GroupElement<T>> PhiFunction(
final MathematicalGroup<E> group, final List<E> baseElements, ...) {
Otherwise, this is indeed more restrictive, as the original signature allows any type, and this one allows only GroupElement<T>, or one of its sub-classes.
As far as how your users use it:
MathematicalGroup mg = new <GroupElement<Object>>MathematicalGroup<Object>(groupElementInstance, ...);
This is an odd use-case. Usually this generics passing is done with static final utility functions.

Why can't javac infer generic type arguments for functions used as arguments?

In the following sample, why is the compiler able to infer the generic arguments for the first call to Foo.create() in Foo.test(), but not able to do so in the second? I'm using Java 6.
public class Nonsense {
public static class Bar {
private static void func(Foo<String> arg) { }
}
public static class Foo<T> {
public static <T> Foo<T> create() {
return new Foo<T>();
}
private static void test() {
Foo<String> foo2 = Foo.create(); // compiles
Bar.func(Foo.create()); // won't compile
Bar.func(Foo.<String>create()); // fixes the prev line
}
}
}
(The compile error is The method func(Nonsense.Foo) in the type Nonsense.Bar is not applicable for the arguments (Nonsense.Foo)).
Note: I understand the compiler error can be fixed by the third line in test() - I'm curious as to whether there is a specific limitation that prevents the compiler from being able to infer the type. It appears to me that there is enough context for it here.
As of Java 7, method overload resolution has to proceed before any target type information from the method you are calling can be taken into account to try to infer the type variable T in the declaration of func. It seems foolish, since we can all see that in this case there is one and only one method named func, however, it is mandated by the JLS and is the behavior of javac from Java 7.
Compilation proceeds as follows: First, the compiler sees that it is compiling a call to a static method of class Bar named func. To perform overload resolution, it must find out what parameters the method is being called with. Despite this being a trivial case, it must still do so, and until it has done so it does not have any information about the formal parameters of the method available to help it. The actual parameters consist of one argument, a call to Foo.create() which is declared as returning Foo<T>. Again, with no criteria from the target method, it can only deduce that the return type is the erasure of Foo<T> which is Foo<Object>, and it does so.
Method overload resolution then fails, since none of the overloads of func is compatible with an actual parameter of Foo<Object>, and an error is emitted to that effect.
This is of course highly unfortunate since we can all see that if the information could simply flow in the other direction, from the target of the method call back towards the call site, the type could readily be inferred and there would be no error. And in fact the compiler in Java 8 can do just that, and does. As another answer stated, this richer type inferencing is very useful to the lambdas that are being added in Java 8, and to the extensions to the Java APIs that are being made to take advantage of lambdas.
You can download a pre-release build of Java 8 with JSR 335 lambdas from the preceding link. It compiles the code in the question without any warnings or errors.
Inferring types from context is too complicated. The main obstacle is probably method overloading. For example, f(g(x)), to determine which f() to apply, we need to know the type of g(x); yet the type of g(x) may need to be inferred from f()'s parameter types. In some languages method overloading is simply prohibited so that type inference can be easier.
In Java 8 your example compiles. The Java team is more motivated to broaden type inference due to lambda expression use cases. It's not an easy task.
The java language specification for java 7 contains 40 pages just to spec method invocation expression (section 15.12)

Why is Class<?> preferred to Class

If I declare a Class as a field:
Class fooClass;
Eclipse gives me the warning:
Class is a raw type. References to
generic type Class should be
parametrized
What does this mean in practice? and why am I urged to do it? If I ask Eclipse for a "quick fix" it gives me:
Class<?> fooClass;
which doesn't seem to add much value but no longer gives a warning.
EDIT: Why is Class generic? Could you please give an example of parameterization, i.e. could there be a valid use of something other than <?> ?
EDIT: WOW! I had not realized the depths of this. I have also watched the Java Puzzler and it's certainly scared me about the bear traps. So I will always use
Class<MyString> myStringClass = MyString.class;
rather than
Class myStringClass = MyString.class;
(But having used Java from day one, I didn't really notice when Class became generic);
NOTE: I have accepted #oxbow_lakes as this makes sense to me, but it is clearly a very complicated area. I would urge all programmers to use the specific Class<MyString> rather than Class. And Class<?> is much safer than Class.
Raw Types and Unbounded Wildcards
None of the previous answers have really addressed why you should prefer Class<?> over Class, as on the face of it, the former seems to offer no more information than the latter.
The reason is that, the raw type, i.e. Class, prevents the compiler from making generic type checks. That is, if you use raw types, you subvert the type-system. For example:
public void foo(Class<String> c) { System.out.println(c); }
Can be called thusly (it will both compile and run):
Class r = Integer.class
foo(r); //THIS IS OK (BUT SHOULDN'T BE)
But not by:
Class<?> w = Integer.class
foo(w); //WILL NOT COMPILE (RIGHTLY SO!)
By always using the non-raw form, even when you must use ? because you cannot know what the type parameter is (or is bounded by), you allow the compiler to reason about the correctness of your program more fully than if you used raw types.
Why have Raw Types at all?
The Java Language Specification says:
The use of raw types is allowed only as a concession to compatibility of legacy code
You should always avoid them. The unbounded wildcard ? is probably best described elsewhere but essentially means "this is parameterized on some type, but I do not know (or care) what it is". This is not the same as raw types, which are an abomination and do not exist in other languages with generics, like Scala.
Why is Class Parameterized?
Well, here is a use-case. Suppose I have some service interface:
public interface FooService
And I want to inject an implementation of it, using a system property to define the class to be used.
Class<?> c = Class.forName(System.getProperty("foo.service"));
I do not know at this point that my class, is of the correct type:
//next line throws ClassCastException if c is not of a compatible type
Class<? extends FooService> f = c.asSubclass(FooService.class);
Now I can instantiate a FooService:
FooService s = f.newInstance(); //no cast
which doesn't seem to add much value
but no longer gives a warning.
You're right. But this might add value:
Class<FooClass> fooClass;
or, if more appropriate:
Class<? extends FooClass> fooClass;
or
Class<FooInterface> fooClass;
As with generics in general, you can improve type safety by specifying what kind of class you want the variable to hold. The warning against raw types is just meant to catch pieces of code where this potential is not used. By declaring Class<?> you're basically saying "this is meant to hold any kind of class".
Because, since JDK 5, Class has now have parameterized type, which makes Class a generic object. This is necessary (since the introduction of Generics) for the compiler to do type checking (at compile time, of course).
Class<?> means a "class of unknown" where ? is a generics wildcard. It means a that fooClass of type Class<?> accepts a Class whose type matches anything.
Typical example:
Class<?> classUnknown = null;
classUnknown = ArrayList.class; //This compiles.
You can, effectively, provided a Parameterized Type to be more specific, e.g.:
Class<ArrayList> = ArrayList.class;
PS Bear in mind, that Class<List> listClass = ArrayList.class; won't compile (even though ArrayList is of List) but (as Mark Peters mentioned on the comment) Class<? extends List> listClass = ArrayList.class; does compile (thanks to the wildcard).
The Javadoc of the Class class does give some idea about why type parameters exist for this class:
T - the type of the class modeled by
this Class object. For example, the
type of String.class is Class<String>.
Use Class<?> if the class being
modeled is unknown.
The use of this type parameter is not so obvious, but a cursory look at the source code of the class, indicates why the type parameter is sometimes necessary. Consider the implementation of the newInstance method:
public T newInstance()
throws InstantiationException, IllegalAccessException
{
if (System.getSecurityManager() != null) {
checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader());
}
return newInstance0();
}
If one hasn't noticed, the type of the object returned by this method is that of the type parameter. This is useful in code that utilizes a lot of reflection, and where one would like to be extra careful to ensure that objects of the right type are being instantiated.
Considering the example in the question, if the class instance was instead declared as:
Class<String> fooClass;
Class<Integer> barClass;
String aString;
then, it is next to impossible to have the following code to compile:
aString = barClass.newInstance();
In short, if you're going to be working with class hierarchies and you wish to impose strict compile time checks to ensure that your code does not need to perform a lot of instanceof checks, then you're better off specifying the type of the class that you wish to utilize. Specifying ? allows all types, but there would be cases when you'll need to be more specific.
Because using raw types instead of parameterized types has many pitfalls.
One of which is that if a raw type is used, all generics on the class are lost. Even those defined per-method.

Categories