If I have a class Foo
:
public class Foo<T> {
public Foo(T t) {
//do someth开发者_如何转开发ing
}
public static <E> void bar(E e) {
//do something
}
}
Why does Foo.bar("String");
infer that E
is a String (and therefore not throw a compiler warning) but new Foo("String");
not infer that T
is a String?
Because the constructor can be considered a special instance method, it is not typed - it gets its type from the class name (with a type parameter),eg Foo<String>
. ie the constructor is not defined as:
public <T> Foo(T t) ...
nor can it be. Doing so would hide the generic type of the class (and you'll get a warning)
The static method however is typed. FYI, the generic-parameter-less call, once the type is inferred, is equivalent to:
Foo.<String>bar("String");
When Java implemented generics, it was decided that a generic class instantiated without type parameters would always return a raw-type. This differs from generic methods missing type parameters, which the compiler tries to infer the type of. From the Java Tutorials:
Generally, the Java compiler can infer the type parameters of a generic method call. Consequently, in most cases, you do not have to specify them.
But when discussion turns to constructors:
Note that to take advantage of automatic type inference during generic class instantiation, you must specify the diamond. In the following example, the compiler generates an unchecked conversion warning because the HashMap() constructor refers to the
HashMap
raw type, not theMap<String, List<String>>
type:
Map<String, List<String>> myMap = new HashMap(); // unchecked conversion warning
source: http://download.oracle.com/javase/tutorial/java/generics/gentypeinference.html
This remains the same in Java 7, however they tried to make it less repetitive by supporting diamond syntax. For example, Foo<String> foo = new Foo<>("String")
. See this section of the same article.
I think you'll need to do this
new Foo<String>("String");
to tell get the generics info passed; similar to the Collections API.
Looking into this, I am going to take a guess here. Consider the following:
public class Foo {
public <E> Foo(E t) {
//do something
}
public static <E> void bar(E e) {
//do something
}
}
In the above class you get no warning when instantiating Foo
as follows:
Foo f = new Foo("String");
This works because the type of E
is being inferred here. Just as you expect it to happen in the method case. However the error you are getting is not because the argument type is not being inferred, but because the raw type for the class cannot be inferred.
I think what this comes down to is that the Class raw type can propagated to the methods, but the methods cannot set the class raw type using inference.
@Kublai Khan's answer is correct; the type of new Foo(s)
is raw Foo
, for backward compatatability.
Java7's constructor diamond type inference ( new Foo<>(s)
) is the same as, and defined in term of, method type inference on method.
http://cr.openjdk.java.net/~darcy/ProjectCoin/ProjectCoin-Documentation-v0.9375.html#diamond
If the class instance creation expression uses "<>" to elide class type arguments, a list of methods m1...mk is defined for the purpose of overload resolution and type argument inference...
...then one of m1...mk is selected, using the process described in §15.12.2 (Determine Method Signature)
Your suspicion is correct, constructors can use inference just like methods, there are no essential differences. You just have to add <>
due to backward compatibility issue.
Why did it take Java 6 years to add this feature is another story.
精彩评论