考虑下面doSomething(List<Object>)接受List<Object>作为参数的方法.
private void doSomething(List<Object> list) {
// do something
}
Run Code Online (Sandbox Code Playgroud)
现在考虑下面的代码片段,它试图调用doSomething()我尝试传递List<String>给的地方doSomething()
List<Object> objectList;
List<String> stringList;
doSomething(stringList); // compilation error incompatible types
doSomething(objectList); // works fine
Run Code Online (Sandbox Code Playgroud)
即使在代码下面也会引发编译错误
objectList = stringList; // compilation error incompatible types
Run Code Online (Sandbox Code Playgroud)
我的问题是为什么List<String>不能传递给接受的方法List<Object>?
考虑一下片段:
Number[] numbers = {1, 2.3, 4.5f, 6000000000000000000L};
Run Code Online (Sandbox Code Playgroud)
完成上述操作Number是完全可以的,是一个抽象类.
继续前进
List<Long> listLong = new ArrayList<Long>();
listLong.add(Long.valueOf(10));
List<Number> listNumbers = listLong; // compiler error - LINE 3
listNumbers.add(Double.valueOf(1.23));
Run Code Online (Sandbox Code Playgroud)
曾3号线被设计成可以编译成功,我们最终会得到一个List的NumberS,即
for(Number num: listNumbers ){
System.out.println(num);
}
// 10
// 1.23
Run Code Online (Sandbox Code Playgroud)
这些都是数字.
我在一本书中看到了这个,
泛型不支持子类型,因为它会导致实现类型安全性的问题.这就是为什么
List<T>不被视为一个亚型List<S>,其中S是超级型T
如上所述,在这种特殊情况下哪种类型的安全性会丢失,第3行是否需要成功编译?
在Java中,为什么以下代码行不起作用?
List<List<String>> myList = new ArrayList<ArrayList<String>>();
Run Code Online (Sandbox Code Playgroud)
它可以工作,如果我改为
List<ArrayList<String>> myList = new ArrayList<ArrayList<String>>();
Run Code Online (Sandbox Code Playgroud)
起初,我想也许你不能有一个界面列表,但我可以创建一个List<Runnable>很好的.
想法?
所以我查看了一些Java代码并偶然发现:
List<? extends SomeObject> l;
Run Code Online (Sandbox Code Playgroud)
基本上这个列表接受SomeObject的某些对象 - SomeObject本身或其继承者.但是根据多态性,它的继承者也可以像SomeObject一样,所以这也可以:
List<SomeObject> l;
Run Code Online (Sandbox Code Playgroud)
那么,当第二个选项明确定义并且几乎相同时,为什么有人会使用第一个选项?
你能解释为什么以下有效吗?
public class GenericsTest<T> {
public void doSomething(T v1, T v2) {
}
public static <T> void doSomethingStatic(T v1, T v2) {
}
public static <T> void doSomethingStaticList(List<T> v1, List<T> v2)
{
}
public static void main(String[] args) {
GenericsTest<String> gt = new GenericsTest<>();
// OK
gt.doSomething("abc", "abc");
// Not OK
gt.doSomething(1, "abc");
// OK
doSomethingStatic(1, 2);
// Still OK
doSomethingStatic(1, "abc");
// So why is this not OK?
List<String> list1=new LinkedList<>();
List<Integer> list2=new LinkedList<>();
doSomethingStaticList(list1,list2);
}
}
Run Code Online (Sandbox Code Playgroud)
T v1, …
为什么必须使用泛型类型Map<?, ? extends List<?>>而不是更简单Map<?, List<?>>的test()方法?
public static void main(String[] args) {
Map<Integer, List<String>> mappy =
new HashMap<Integer, List<String>>();
test(mappy);
}
public static void test(Map<?, ? extends List<?>> m) {}
// Doesn't compile
// public static void test(Map<?, List<?>> m) {}
Run Code Online (Sandbox Code Playgroud)
注意到以下工作,并且三种方法无论如何都具有相同的擦除类型.
public static <E> void test(Map<?, List<E>> m) {}
Run Code Online (Sandbox Code Playgroud) 原始列表转换为List<?>正常.为什么原始列表列表不能转换为列表List<?>?
{ // works
List raw = null;
List<?> wild = raw;
}
{ // Type mismatch: cannot convert from List<List> to List<List<?>>
List<List> raw = null;
List<List<?>> wild = raw;
}
Run Code Online (Sandbox Code Playgroud)
背景故事(缓解xy问题):
我正在使用的API返回List<JAXBElement>.我碰巧知道它总是如此List<JAXBElement<String>>.我计划循环并构建自己的List<String>,但我在写时试图修复(但不是抑制)原始类型编译器警告List<JAXBElement> raw = api();.
我试过了:
List<JAXBElement<?>> raw = api();
List<JAXBElement<?>> raw = (List<JAXBElement<?>>) api();
Run Code Online (Sandbox Code Playgroud)
但这些给出了类型不匹配错误.
有趣的是,这没有任何警告或错误:
for (JAXBElement<?> e : api()) {
// ...
}
Run Code Online (Sandbox Code Playgroud) 我知道为什么不应该这样做.但有没有办法向外行人解释为什么这是不可能的.你可以轻松地向外行解释:Animal animal = new Dog();.狗是一种动物,但是一系列的狗并不是动物的清单.
我正在流式传输实现接口的类的对象。我想将它们收集为接口的元素列表,而不是实现类。
这对于 Java 16.0.1 的Stream#toList方法似乎是不可能的。例如在下面的代码中,最后一条语句将无法编译。
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class WhyDodo {
private interface Dodo { }
private static class FancyDodo implements Dodo { }
public static void main(String[] args) {
final List<Dodo> dodos = Stream.of(new FancyDodo()).collect(Collectors.toList());
final List<FancyDodo> fancyDodos = Stream.of(new FancyDodo()).toList();
final List<Dodo> noFancyDodos = Stream.of(new FancyDodo()).toList();
}
}
Run Code Online (Sandbox Code Playgroud)
我们可以显式地将每个元素从 转换FancyDodo为Dodo。但至少为了简洁起见,我们也可以使用.collect(Collectors.toList()).
为什么我不能使用 Stream#toList 在 Java 16 中收集类的接口列表?
如果有人有比明确投射更好的解决方案,我也很乐意听到:)
请考虑以下代码:
class AA { }
class BB extends AA { }
public class Testing {
public static void main(String[] args) {
BB[] arr = new BB[10];
AA[] arr2 = arr;
BB b = new BB();
AA a = new AA();
arr2[0] = a; // ArrayStoreException at runtime
arr2[1] = b;
List<BB> listBB = new ArrayList<>();
List listAA = listBB;
listAA.add("hello world.txt");
}
}
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,ArrayStoreException我尝试了arr2[0] = a.这意味着数组会记住它必须接受的类型.但是List不记得他们了.它只是编译并运行良好.在ClassCastException当我检索对象将被抛出BB.
所以问题是:
数组如何记住它的类型(我知道它被称为"具体化").这究竟是怎么发生的?
并且为什么只有阵列才能获得这种功率,但是ArrayList …
java ×10
generics ×9
arraylist ×2
casting ×2
arrays ×1
covariance ×1
java-16 ×1
java-stream ×1
list ×1
oop ×1
polymorphism ×1
raw-types ×1