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

泛型是如何工作的?

公孙宏远
2023-03-14
问题内容

虽然我确实了解泛型的一些特殊情况,但以下示例缺少一些内容。

我有以下课程

1 public class Test<T> {
2   public static void main(String[] args) {
3     Test<? extends Number> t = new Test<BigDecimal>();
4     List<Test<? extends Number>> l =Collections.singletonList(t);
5   }
6 }

第4行给我错误

Type mismatch: cannot convert from List<Test<capture#1-of ? extends Number>> 
to List<Test<? extends Number>>`.

显然,编译器认为差异?实际上并不相等。虽然我的直觉告诉我,这是正确的。

谁能提供一个示例,如果第4行合法,我会遇到运行时错误?

编辑:

为避免混淆,我将第=null3行中的in 替换为一个具体的作业


问题答案:

正如肯尼(Kenny)在评论中指出的那样,您可以通过以下方法解决此问题:

List<Test<? extends Number>> l =
    Collections.<Test<? extends Number>>singletonList(t);

这立即告诉我们,该操作并非 不安全 ,只是 推理 受限的受害者。如果不安全,则以上内容将无法编译。

由于仅在上述通用方法中使用显式类型参数才有必要充当 提示 ,因此我们可以推测,此处所要求的显式类型参数是推理引擎的技术限制。实际上,Java
8编译器目前预计将对类型推断进行许多改进。我不确定您的具体情况是否会解决。

那么,实际发生了什么?

那么,编译错误我们得到节目类型参数TCollections.singletonList被推断为capture<Test<? extends Number>>。换句话说,通配符具有一些与之关联的元数据,可将其链接到特定的上下文。

  • 想到捕获通配符(capture<? extends Foo>)的最佳方法是将其作为具有相同界限的 未命名 类型参数(即<T extends Foo>,但无法引用T)。
  • “释放”捕获功能的最佳方法是将其绑定到通用方法的命名类型参数。我将在下面的示例中对此进行演示。有关更多信息,请参见Java教程“通配符捕获和帮助程序方法”(感谢@WChargin的引用)。

假设我们要有一个移动列表的方法,将其包装在后面。然后,假设我们的列表具有未知(通配符)类型。

public static void main(String... args) {
    List<? extends String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
    List<? extends String> cycledTwice = cycle(cycle(list));
}

public static <T> List<T> cycle(List<T> list) {
    list.add(list.remove(0));
    return list;
}

这可以正常工作,因为T解决了capture<? extends String>,而不是? extends String。如果我们改为使用以下非通用实现循环:

public static List<? extends String> cycle(List<? extends String> list) {
    list.add(list.remove(0));
    return list;
}

它将无法编译,因为我们尚未通过将捕获分配给类型参数来使其可访问。

因此,这开始解释了为什么of的使用者singletonList将从类型推断器解析T为中受益Test<capture<? extends Number>,并因此返回a List<Test<capture<? extends Number>>>而不是a List<Test<? extends Number>>

但是为什么不能将一个分配给另一个呢?

为什么我们不能仅将a分配List<Test<capture<? extends Number>>>List<Test<? extends Number>>

好吧,如果我们考虑到capture<? extends Number>一个匿名类型参数的上限等于的事实Number,那么我们可以将这个问题变成“为什么下面的代码不编译?” (不是!):

public static <T extends Number> List<Test<? extends Number>> assign(List<Test<T>> t) {
    return t;
}

这有一个不编译的充分理由。如果确实如此,那么这将是可能的:

//all this would be valid
List<Test<Double>> doubleTests = null;
List<Test<? extends Number>> numberTests = assign(doubleTests);

Test<Integer> integerTest = null;
numberTests.add(integerTest); //type error, now doubleTests contains a Test<Integer>

那么为什么显式工作呢?

让我们回到起点。如果以上内容不安全,则允许这样做:

List<Test<? extends Number>> l =
    Collections.<Test<? extends Number>>singletonList(t);

为此,它意味着允许以下操作:

Test<capture<? extends Number>> capturedT;
Test<? extends Number> t = capturedT;

嗯,这不是有效的语法,因为我们无法显式引用捕获,因此让我们使用与上述相同的技术对其进行评估!让我们将捕获绑定到“ assign”的另一个变体上:

public static <T extends Number> Test<? extends Number> assign(Test<T> t) {
    return t;
}

这样编译成功。不难看出为什么它应该是安全的。这是类似

List<? extends Number> l = new List<Double>();


 类似资料:
  • 问题内容: 在查找(测试)信息时,我遇到了一些问题,完全不知道为什么会发生。现在,我知道没有实际的理由执行此操作,这绝对是可怕的代码,但是为什么行得通呢? 因此,基本上,我将对象添加到Quods的ArrayList中。现在,我看到java如何无法有效地检查它,因为它必须浏览所有引用,而这些引用可能甚至都没有存储在任何地方。但是为什么get()起作用了。get()是否不是要返回Quod的实例,就像在

  • 在C 14标准中,通用lambda如何工作(关键字作为参数类型)? 它是基于C模板,其中编译器为每个不同的参数类型生成一个具有相同主体但替换类型的新函数(编译时多态性),还是更类似于Java的泛型(类型擦除)? 代码示例:

  • 问题内容: 我尝试用泛型创建一个抽象类。业务逻辑是将文本从一种语言翻译成另一种语言。每种语言都必须有一个课程。对于每种语言,我还需要一个类,该类是方法的返回对象,它具有参数。T应该是(例如EnglishText,ChineseText等)的子类。那就是我奋斗的地方。您如何编码此约束? 我创建了以下超级类。 如果我误解了泛型的概念或将其错误地使用,请说明一下。谢谢。 问题答案: 只需将其添加到定义泛

  • 我刚刚开始在Haskell中编程,我遇到了以下定义:

  • 应用程序具有上下文路径-->/spring-form-simple-project 因此,为了访问,我使用: 这个控制器又返回student.jsp,当提交这个student.jsp时,它用-->@RequestMapping(value=“/AddStudent”,method=RequestMethod.post)调用controller 任何关于这通常如何工作的指示都将是有帮助的。 谢谢!

  • 我试图实现一个接受泛型参数的函数定义,只要它扩展了另一个特定的泛型类型。简言之参数A必须扩展参数B,其中A和B都是泛型的。 下面是我的示例 用法示例如下 一些封闭的班级 和函数调用 我不想在抽象类声明中定义E,因为T已经在那里定义了。 我也试着做了以下几点: 将myList定义为接受扩展T的键 将E定义为T类型(无法找到如何指定它在函数中扩展T 但它从来都不起作用。有没有办法做到这一点?我在Sta