当前位置: 首页 > 面试题库 >

泛型,类型参数和通配符

马哲
2023-03-14
问题内容

我试图理解Java泛型,它们似乎很难理解。例如,这很好…

public class Main {

    public static void main(String[] args) {
        List<?> list = null;
        method(list);
    }

    public static <T> void method(List<T> list) { }
}

…就是这个…

public class Main {

    public static void main(String[] args) {
        List<List<?>> list = null;
        method(list);
    }

    public static <T> void method(List<T> list) { }
}

… 还有这个 …

public class Main {

    public static void main(String[] args) {
        List<List<List<?>>> list = null;
        method(list);
    }

    public static <T> void method(List<List<T>> list) { }
}

…但是不能编译:

public class Main {

    public static void main(String[] args) {
        List<List<?>> list = null;
        method(list);
    }

    public static <T> void method(List<List<T>> list) { }
}

有人可以用简单的语言解释发生了什么吗?


问题答案:

对于泛型类型,主要要了解的是它们不是协变的。

因此,尽管您可以这样做:

final String string = "string";
final Object object = string;

以下内容将无法编译:

final List<String> strings = ...
final List<Object> objects = strings;

这是为了避免您绕过通用类型的情况:

final List<String> strings = ...
final List<Object> objects = strings;
objects.add(1);
final String string = strings.get(0); <-- oops

因此,一一讲解您的示例

1个

您的通用方法采用a
List<T>,而您采用List<?>;(基本上是)List<Object>T可以分配给Object类型,并且编译器满意。

2

您的通用方法相同,您传入List<List<?>>T可以分配给List<?>类型,编译器再次感到高兴。

3

这与 2 基本上相同,但具有另一层嵌套。T仍然是List<?>类型。

4

这是有点梨形的地方,也是我从上面提出的观点。

您的通用方法采用List<List<T>>。您通过了List<List<?>>。现在,由于通用类型不是协变的,List<?>因此无法将其分配给List<T>

实际的编译器错误(Java 8)是:

必填:java.util.List<java.util.List<T>> 找到:
java.util.List<java.util.List<?>> 原因:无法推断类型变量T
(参数不匹配;java.util.List<java.util.List<?>>无法转换为java.util.List<java.util.List<T>>

基本上,编译器告诉您,T由于必须推断List<T>外部列表中嵌套的类型,因此找不到分配的对象。

让我们更详细地看一下:

List<?>List一些未知类型 -它可以是一个List<Integer>或一个List<String>;
我们可以get这样做Object但是我们不能add。因为否则我们会遇到我提到的协方差问题。

List<List<?>>ListList一些未知类型的-
它可能是一个List<List<Integer>>或一个List<List<String>>。在情况 1中
,可以分配T给通配符列表,Object但不允许add对通配符列表进行操作。在第 4种 情况下,此操作无法完成-
主要是因为没有通用的构造可以防止add外部攻击List

如果在第二种情况下将编译器分配TObject,则可能会出现如下所示的情况:

final List<List<Integer>> list = ...
final List<List<?>> wildcard = list;
wildcard.add(Arrays.asList("oops"));

因此,由于协方差,不可能安全地将a分配List<List<Integer>>任何其他 泛型List



 类似资料:
  • 问题内容: 我正在刷新有关Java泛型的知识。因此,我转向了Oracle的优秀教程……并开始为我的同事编写一个演示文稿。我在本教程中遇到了有关通配符的部分,内容为: 考虑以下方法,printList: printList的目标是打印任何类型的列表,但未能实现该目标- 它仅打印Object实例的列表;它不能打印,,,等等,因为它们不是的亚型。要编写通用的printList方法,请使用: 我知道那是行

  • 我正在刷新我关于Java泛型的知识。所以我转向甲骨文的优秀教程...并开始为我的同事准备一个演示文稿。我在教程中遇到了通配符部分,上面写着: 考虑以下方法,打印列表: printList的目标是打印任何类型的列表,但它无法实现该目标-它只打印对象实例的列表;无法打印列表 我明白那个

  • 在了解Java泛型的过程中,我遇到了以下问题: 假设我有下面的方法来添加列表的元素,只限于包含数字的列表。 但是这段代码和这段代码有什么不同: 它们都按预期编译和执行。这两者之间有什么区别?除了语法之外?什么时候我更喜欢使用通配符而不是前者? 是的,使用通配符方法,我不能在列表中添加除null之外的新元素,否则它将无法编译。除此之外呢?

  • 所以我看了官方的java教程,https://docs.oracle.com/javase/tutorial/java/generics/index.html,也搜索了stackoverflow,结果发现使用

  • 可以使用泛型将返回类型与参数类型匹配吗? 实例案例: 我有一个抽象类,可以从不同的POJO导入数据,这个类包含一个abstract方法importData。 importData返回的对象必须与传递给该方法的对象类型相同。 由于抽象方法的每个实现的对象类型不同,并且类型不扩展另一个,如何定义抽象方法,以便实现返回类型和传递类型必须匹配? 经过考验: 结果: 方法的返回类型不必与传递的对象类型匹配。

  • 问题内容: 我有一个方法以a 作为参数。 在中,我如何知道a 是还是a 是? 问题答案: 根据用户omain的回答“如果使用<?>,则意味着您将不会在任何地方使用参数化类型。要么转到特定类型(在您的情况下,似乎是),要么转到非常通用的“ 另外,我相信如果您使用问号,编译器将在运行时(类型;有效Java的第119页)消除类型不匹配的情况,绕过擦除,并有效地消除了使用泛型类型所带来的好处? 要回答发问