通配符可用于各种情况:作为参数,字段或局部变量的类型 ; 有时作为返回类型 (虽然更好的编程实践更具体).
我在下面的类中尝试了所有四个,并且在每个类中都有编译器错误.为什么?我究竟做错了什么?
public class MainClass {
private ? instanceFieldWithWildCardType;//ERROR
private static ? staticFieldWithWildCardType;//ERROR
private void methodWithWildCardParam(? param) {}//ERROR
private void methodWithWildCardLocalVariable() {
? localVariableWithWildCardType;//ERROR
}
private ? methodWithWildCardReturnType() {//ERROR
return null;
}
private void methodWithWildCardParam(? param) {}//ERROR
}
Run Code Online (Sandbox Code Playgroud) 假设我有一个方法"mix",它采用两个可能不同类型的T和S列表,并返回一个包含两者元素的List.对于类型安全,我想指定返回的List是R类型,其中R是T和S共有的超类型.例如:
List<Number> foo = mix(
Arrays.asList<Integer>(1, 2, 3),
Arrays.asList<Double>(1.0, 2.0, 3.0)
);
Run Code Online (Sandbox Code Playgroud)
为了指定这个,我可以将方法声明为
static <R, T extends R, S extends R> List<R> mix(List<T> ts, List<S> ss)
Run Code Online (Sandbox Code Playgroud)
但是如果我想在类上创建mix一个实例方法而不是静态List2<T>呢?
<R, T extends R, S extends R> List<R> mix ...
Run Code Online (Sandbox Code Playgroud)
阴影<T>在实例上List2,所以没有好处.
<R, T extends S&T, S extends R> List<R> mix ...
Run Code Online (Sandbox Code Playgroud)
解决了阴影问题,但编译器不接受
<R super T, S extends R> List<R> mix ...
Run Code Online (Sandbox Code Playgroud)
被编译器拒绝,因为下限通配符不能存储在命名变量中(仅用于? super X表达式)
我可以将参数移动到类本身,例如List2<R, T extends R, S extends R>,但类型信息实际上没有业务存在于实例级别,因为它仅用于一个方法调用,并且您每次需要时都必须重新构建对象在不同的参数上调用该方法. …
(要清除问题,'T'指的是在Class中声明的类型参数)
举个例子,请查看以下应用程序:
public class TestClass {
interface InterfaceA{}
interface InterfaceB{}
interface InterfaceC{}
class ClassA implements InterfaceA, InterfaceB, InterfaceC{}
public static void main(String[] args){
Class<? super ClassA> superClass1 = ClassA.class;
Class<? super ClassA> superclass2 = InterfaceA.class;
Class<? super ClassA> superclass3 = InterfaceB.class;
Class<? super ClassA> superclass4 = InterfaceC.class;
for(Class<?> clazz : ClassA.class.getInterfaces()){
System.out.println(clazz.getName());
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出是人们所期望的:
TestClass$InterfaceA
TestClass$InterfaceB
TestClass$InterfaceC
Run Code Online (Sandbox Code Playgroud)
因此,基于上面的这个例子,我们可以看到编译器认识到InterfaceA 确实符合通配符的边界,所有其他接口也是如此.
直观地说,我希望以下是安全的,但它不是:
Class<? super ClassA>[] interfaces = ClassA.class.getInterfaces();
Run Code Online (Sandbox Code Playgroud)
编译器发出警告,因为签名表示它返回Class; 但是,Class的javadoc声明如下:
如果此对象表示类,则返回值是一个数组,其中包含表示该类实现的所有接口的对象.
'这个对象'在我们的例子中指的是ClassA.基于此声明,如果我们致电:
ClassA.class.getInterfaces()
Run Code Online (Sandbox Code Playgroud)
然后我们从逻辑上知道Class<?>返回数组中的每一个都将包含对超类型的引用ClassA …
我即将创建一个工厂,它创建某种类型的对象T,它扩展了某个类A和另一个接口I.但是,T必须是未知的.以下是最低声明:
public class A { }
public interface I { }
Run Code Online (Sandbox Code Playgroud)
这是工厂方法:
public class F {
public static <T extends A & I> T newThing() { /*...*/ }
}
Run Code Online (Sandbox Code Playgroud)
这编译所有罚款.
当我尝试使用该方法时,以下工作正常:
A $a = F.newThing();
Run Code Online (Sandbox Code Playgroud)
......虽然这不是:
I $i = F.newThing();
Run Code Online (Sandbox Code Playgroud)
编译器抱怨:
绑定不匹配:类型F的泛型方法newThing()不适用于arguments().推断的类型I和A不是有界参数的有效替代
我不明白为什么.明确指出"newThing返回某种类型的东西T,它确实扩展了A类并实现了接口I".当分配给一个一切正常(因为T延伸的),但分配给我并没有(因为什么?,清楚地返回的东西是既是一个和一个I)
另外:当返回一个对象时,比如说类型B class B extends A implements I,我需要将它转换为返回类型T,尽管B匹配边界:
<T extends A & I> T newThing() {
return (T) new B();
}
Run Code Online (Sandbox Code Playgroud)
但是,编译器不会抛出任何警告,如UncheckedCast等.
因此我的问题:
-
编辑:这里完整的代码片段完全使用Eclipse 3.7,项目设置为JDK 6: …
请解释这个通用代码通配符编译时错误:
//no compile time error.
List<? extends Number> x = new ArrayList<>();
//compile time error.
List<? extends Number> x = new ArrayList<? extends Number>();
Run Code Online (Sandbox Code Playgroud) 我想知道为什么这段代码编译成功?
源代码:
abstract class A<K extends Number>
{
public abstract <M> A<? super M> useMe(A<? super M> k);
}
Run Code Online (Sandbox Code Playgroud)
编译成功
它是如何工作的,为什么编译?M是任何类型,为什么它可以使用?应该是:<M extends Number>?这不会编译:
abstract class A<K extends Number>
{
public abstract <M> A<? super M> useMe(A<M> k);
}
Run Code Online (Sandbox Code Playgroud)
错误信息:
类型参数M不在类型变量K的范围内,其中M,K是类型变量:M extends方法useMe中声明的Object(A)K extends A类中声明的Number
有什么不同?
所以我有这个方法:
protected void collectSelectedItems(ListSelectionModel lsm,
Collection<? super MyItemClass> result) {
for (int i : GUI.getSelectionIndices(lsm))
{
result.add(getItemByDisplayIndex(i));
}
}
Run Code Online (Sandbox Code Playgroud)
我想返回集合而不是void方法:
protected <T super MyItemClass> Collection<T>
collectSelectedItems(ListSelectionModel lsm, Collection<T> result) {
for (int i : GUI.getSelectionIndices(lsm))
{
result.add(getItemByDisplayIndex(i));
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
意图做这样的事情(在哪里MyItemClass extends MyItemBaseClass):
List<MyItemBaseClass> list =
collectSelectedItems(lsm, new ArrayList<MyItemBaseClass>());
Run Code Online (Sandbox Code Playgroud)
但我得到一个语法错误super:
令牌"super"上的语法错误,预期
是什么赋予了?我能解决这个问题吗?
我有一堂课:
class Generic<T> {
List<List<T>> getList() {
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
当我Generic用通配符和调用getList方法声明 a时,以下赋值是非法的。
Generic<? extends Number> tt = null;
List<List<? extends Number>> list = tt.getList(); // this line gives compile error
Run Code Online (Sandbox Code Playgroud)
这对我来说似乎很奇怪,因为根据 的声明Generic,很自然地创建 aGeneric<T>并获得 a List<List<T>>when call getList。
事实上,它需要我像这样写作业:
List<? extends List<? extends Number>> list = tt.getList(); // this one is correct
Run Code Online (Sandbox Code Playgroud)
我想知道为什么第一个是非法的,为什么第二个是合法的。
我给出的例子只是一些示例代码来说明问题,您不必关心它们的含义。
错误信息:
不兼容的类型:
必需:List<java.util.List<? extends java.lang.Number>>
找到:List<java.util.List<capture<? extends java.lang.Number>>>
当我尝试编译以下代码时:
LinkedList<List<? extends Number>> numList = new LinkedList<List<Integer>>();
Run Code Online (Sandbox Code Playgroud)
我得到一个不兼容的类型错误:
Required: LinkedList <java.util.list<? extends java.lang.Number>>
Found: LinkedList <java.util.list<Integer>>
Run Code Online (Sandbox Code Playgroud)
如何实现LinkedList包含具有List扩展元素的元素Number?
为了清楚起见,我希望以numList下列方式添加列表:
numList.add(new LinkedList<Integer>());
为什么这段代码有效
ArrayList<?>[] arr = new ArrayList<?>[2];
Run Code Online (Sandbox Code Playgroud)
但以下两个不是?
ArrayList<? extends Object>[] arr = new ArrayList<? extends Object>[2];
ArrayList<? super Object>[] arr = new ArrayList<? super Object>[2];
Run Code Online (Sandbox Code Playgroud)
最后两行生成编译错误;
错误:通用数组创建.
请澄清差异.
另一方面ArrayList<?>[] arr = new ArrayList<?>[2];编译好但是
ArrayList<?> arr = new ArrayList<?>();
Run Code Online (Sandbox Code Playgroud)
不.