当前位置: 首页 > 知识库问答 >
问题:

仿制药中的仿制药是如何工作的?

刁跃
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行是合法的,谁能提供一个我会得到运行时错误的例子?

编辑:

为了避免混淆,我用一个具体的赋值替换了第3行中的=null

共有3个答案

杜俭
2023-03-14

也许这可以解释编译器的问题:

List<? extends Number> myNums = new ArrayList<Integer>();

这个属型通配符列表可以保存从Number扩展的任何元素。所以可以给它分配一个整数列表。但是现在我可以给myNums添加一个双精度,因为双精度也从Number扩展,这将导致运行时问题。所以编译器禁止对myNums的每一次写访问,我只能在上面使用读方法,因为我只知道我得到的任何东西都可以转换为Number。

因此,编译器抱怨使用这种通配符泛型可以做很多事情。有时他对你能保证安全的事情很生气。

但幸运的是,有一个技巧可以绕过这个错误,这样你就可以自己测试什么可能会打破这个错误:

public static void main(String[] args) {

    List<? extends Number> list1 = new ArrayList<BigDecimal>();
    List<List<? extends Number>> list2 = copyHelper(list1);


}

private static <T> List<List<T>> copyHelper(List<T> list) {
    return Collections.singletonList(list);

}
向苗宣
2023-03-14

没有潜在的运行时错误,只是超出了编译器静态确定错误的能力。每当你引起类型推断时,它就会自动生成一个新的

因此,如果通过指定

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

它很好用。生成的代码与您的调用是合法的没有什么不同,这只是编译器的一个限制,它自己无法解决这个问题。

推理创建捕获和捕获不兼容的规则阻止了本教程示例编译,然后在运行时崩溃:

public static void swap(List<? extends Number> l1, List<? extends Number> l2) {
    Number num = l1.get(0);
    l1.add(0, l2.get(0));
    l2.add(0, num);
}

是的,语言规范和编译器可能会变得更复杂,以区分您的示例,但事实并非如此,而且它足够简单,可以解决这个问题。

督俊雅
2023-03-14

正如肯尼在评论中指出的,你可以通过以下方式绕过这个问题:

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

这立即告诉我们,操作并不不安全,它只是有限推断的受害者。如果它是不安全的,上述内容就无法编译。

由于在上述泛型方法中使用显式类型参数只是作为提示,因此我们可以推测,这里需要它是推理机的一个技术限制。事实上,Java 8编译器目前计划对类型推断进行许多改进。我不确定你的具体案件是否会得到解决。

我们得到的编译错误表明集合的类型参数T。singletonList被推断为捕获

  • 认为通配符捕获的最佳方式(捕获

假设我们想要一种方法,将列表移到后面。然后假设我们的列表具有未知(通配符)类型。

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被解析为捕获

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

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

因此,这就开始解释为什么singletonList的消费者会受益于类型推断器将T解析为测试

为什么我们不能分配一个列表

如果我们考虑到<代码>捕获的事实

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;

这不是有效的语法,因为我们不能显式引用捕获,所以让我们使用与上面相同的技术来评估它!让我们将捕获绑定到“分配”的另一个变体:

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

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

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

 类似资料:
  • 问题内容: JSR-299规范在第3.1节中规定: 如果托管bean类是泛型类型,则它必须具有范围@Dependent。如果带有参数化Bean类的托管Bean声明了@Dependent以外的任何范围,则容器将自动检测到该问题并将其视为定义错误。 有效地意味着您不能这样做: 做出此决定的技术原因是什么? 是否会在即将发布的CDI版本中对其进行补救? 有解决此问题的最佳实践吗? 谢谢 编辑 -我经常使

  • 问题内容: 我正在尝试模拟Spring Rest 的方法。 在同一测试中,我有多个调用,它们的区别仅在于返回类型。 这是我创建的模拟方法 第一 第二 模拟不考虑的通用参数,最后定义的模拟胜过前者。 有什么办法可以使其工作? 问题答案: Mockito并不擅长匹配泛型本身,但是您的解决方案比一般情况要容易得多。 更换: 与: 首先,不匹配类型,甚至不匹配类型(从Mockito 1.x开始)。匹配所有

  • 我正在尝试结合Guice的这3个功能:注入,多重绑定,泛型。我创建了一个生产项目的原型,所以这里是: 首先,这是泛型的一个小层次结构(在生产案例中,有N个实体的层次结构): 接下来,类ToCreate1和ToCreate2我想创建的工厂。 基类: 它的继承者: 然后,工厂本身: 所以,现在我想注入一个map,包含< i>Factory 因此,我使用配置方法创建了Guice的抽象模块: 所以,我注入

  • 我使用通配符和列表上的下界泛型,但编译器抛出错误。 代码: 错误: 类型列表中的add(capture#8-of?super Integer)方法不适用于参数(Number) 与<代码>列表

  • 大概记得: 1,讲讲redis的zset 2,讲讲如何使用zset实现排行榜 3,讲讲线程池的核心参数以及线程池运行流程 4,讲讲redis缓存雪崩 5,讲讲缓存一致性,项目中如何使用主动更新+超时剔除的方案? 6,讲讲mybatis插入数据后如何获取主键id 完全没准备,八股不熟,项目基本忘光了疯狂拷打,不过面试官真的很好 #面经#

  • 我是一个相当新开发的新泽西客户,在一些测试中遇到了一些问题。首先,我可能应该提到,我的应用程序都是客户端的,根本没有服务器端的东西。 我的问题是,我想创建实例< code > InboundJaxrsResponse 的< code>Response对象,到目前为止,我一直试图通过使用< code>Mockito和< code > Response builder . build()模拟< cod