为什么必须使用泛型类型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) 我主要是一名C#开发人员,我正在向我的朋友教授Data Structures,他们在他们的大学里使用Java,我在Java中看到了这样一个表达式:
void printCollection(Collection<?> c) {
for (Object e : c) {
System.out.println(e);
}
}
Run Code Online (Sandbox Code Playgroud)
我还没有在C#中看到这样的东西,所以我想知道Java Collection<T>
和Collection<?>
Java 之间的区别是什么?
void printCollection(Collection<T> c) {
for (Object e : c) {
System.out.println(e);
}
}
Run Code Online (Sandbox Code Playgroud)
我认为它也可以用上面的方式编写.在文档中的家伙比较Collection<Object>
和Collection<T>
虽然.
示例来自http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
我正在阅读关于泛型的内容,我不明白是否需要未绑定的通配符以及它与原始类型的区别.我读了这个问题,但仍然没有弄清楚.在未绑定的通配符的 Java教程页面中,我得到了以下两点,我没有理解第一点:
- 如果您正在编写可以使用
Object
类中提供的功能实现的方法.- 当代码使用泛型类中不依赖于类型参数的方法时.例如,
List.size()
或List.clear()
.事实上,Class<?>
经常使用,因为大多数方法Class<T>
都不依赖T
.
有人可以用外行语言解释未绑定的通配符和原始类型之间的区别.
有List<?>
什么不同List<Object>
?
我是Java新手.在本 文档中,他们将此作为使用通配符的用例:
static void printCollection(Collection c) {
Iterator i = c.iterator();
for (int k = 0; k < c.size(); k++) {
System.out.println(i.next());
}
}
Run Code Online (Sandbox Code Playgroud)
这是他们的解决方案:
static void printCollection(Collection<?> c) {
for (Object e : c) {
System.out.println(e);
}
}
Run Code Online (Sandbox Code Playgroud)
但是我可以在没有外卡的情况下做同样的事情:
static <T> void printCollection(Collection<T> c) {
Iterator i = c.iterator();
for (int k = 0; k < c.size(); k++) {
System.out.println(i.next());
}
}
Run Code Online (Sandbox Code Playgroud)
有人可以告诉我一个简单的用例,常规的泛型不起作用,但外卡会吗?
更新:此处的答案何时在Java Generics中使用通配符?不要告诉我们需要通配符.事实上,它是另一种方式.
我相信?
泛型中的类型是一种特定的未知类型.这意味着,声明让我们说这种类型的列表会阻止我们在其中添加任何类型的对象.
List<?> unknownList;
unknownList.add(new Object()); // This is an error.
Run Code Online (Sandbox Code Playgroud)
编译器按预期给出错误.
但是当未知类型是二级泛型时,编译器似乎并不关心.
class First<T> {}
List<First<?>> firstUnknownList;
// All these three work fine for some reason.
firstUnknownList.add(new First<>());
firstUnknownList.add(new First<Integer>());
firstUnknownList.add(new First<String>());
Run Code Online (Sandbox Code Playgroud)
我想可能编译器根本不关心二级中的泛型参数,但事实并非如此,
List<First<Integer>> firstIntegerList;
firstIntegerList.add(new First<String>()); // This gives a compiler error as expected.
Run Code Online (Sandbox Code Playgroud)
那么,为什么编译器允许我们在第二个例子中只接受一个未知元素(因而没有任何东西)时添加任何类型的元素?
注意:编译器Java 1.8
通配符可用于各种情况:作为参数,字段或局部变量的类型 ; 有时作为返回类型 (虽然更好的编程实践更具体).
我在下面的类中尝试了所有四个,并且在每个类中都有编译器错误.为什么?我究竟做错了什么?
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) 我没有得到这个代码编译的方式:
List<List> a = new ArrayList();
List<List<?>> b = new ArrayList();
a = b; // incompatible types
b = a; // incompatible types
Run Code Online (Sandbox Code Playgroud)
似乎java 在泛型方面不考虑List
并且List<?>
是相同的类型.
这是为什么?还有一些不错的出路吗?
有一个带有以下签名的库函数:public <T> Set<Class<? extends T>> getSubTypesOf(final Class<T> type)
.这适用于作为参数传递的简单类型,但在泛型的情况下,结果不会使用通配符进行参数化,从而导致javac抱怨原始类型.我想将结果传播到我的应用程序的其余部分,Set<Class<? extends GenericTypeHere<?>>>
但是简单的转换不能像我期望的那样工作.
感谢您的回答,以下是我最终的工作方式:
@SuppressWarnings({"rawtypes", "unchecked"})
private static Set<Class<? extends GenericTypeHere<?>>> factoryTypes() {
return (Set) new Reflections("...").getSubTypesOf(GenericTypeHere.class);
}
Run Code Online (Sandbox Code Playgroud) I was reading this article, , about subclassing a builder class. I understood the article but there was one small bit that bothered me. There was this method,
public static Builder<?> builder() {
return new Builder2();
}
Run Code Online (Sandbox Code Playgroud)
When I changed Builder<?>
to Builder
, a raw type, the compiler would not compile the code. The error was,
Rectangle.java:33: error: cannot find symbol
System.out.println(Rectangle.builder().opacity(0.5).height(250);
Run Code Online (Sandbox Code Playgroud)
What was the additional information passed to the compiler using the additional <?>
? I suspected it …
下面的方法编译没有问题:
static Stream<Optional<? extends Number>> getNumbers(Stream<Number> numbers) {
return numbers.map(Optional::of);
}
Run Code Online (Sandbox Code Playgroud)
但如果我像这样添加一个简单的过滤:
static Stream<Optional<? extends Number>> getNumbers2(Stream<Number> numbers) {
return numbers.map(Optional::of).filter(number -> true);
}
Run Code Online (Sandbox Code Playgroud)
它会生成以下错误:
不兼容的类型:
java.util.stream.Stream<java.util.Optional<java.lang.Number>> 无法转换为
java.util.stream.Stream<java.util.Optional<? 扩展 java.lang.Number>>
在 openJdk-11 和 openJdk-17 上进行了测试。
我希望它们都做同样的事情(要么都编译正常,要么都生成相同的编译错误),所以我真的对此感到困惑:这里的一般规则是什么解释了为什么第一种方法编译正常而第二种方法编译正常才不是?谢谢!
generics ×10
java ×10
casting ×2
raw-types ×2
collections ×1
java-stream ×1
types ×1
wildcard ×1