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

Java泛型地狱

唐沈义
2023-03-14
问题内容

我怀疑以前曾在这里问过(并回答过),但我不知道该如何命名。为什么仅当我不通过类本身时才可以毫无问题地表达通配符?

一切都归结为这段代码。一切正常,除了对的调用genericsHell(ShapeSaver.class)

interface Shape { }

interface Circle extends Shape { }

interface ShapeProcessor<T extends Shape> { }

class CircleDrawer implements ShapeProcessor<Circle> { }

class ShapeSaver<T extends Shape> implements ShapeProcessor<T> { }

class Test {
    void genericsHeaven(ShapeProcessor<? extends Shape> a) {}

    void genericsHell(Class<? extends ShapeProcessor<? extends Shape>> a) {}

    void test() {
        genericsHeaven(new CircleDrawer());
        genericsHeaven(new ShapeSaver<Circle>());
        genericsHell(CircleDrawer.class);
        genericsHell(ShapeSaver.class); // ERROR: The method genericsHell is not applicable for the arguments (Class<ShapeSaver>)
    }
}

问题答案:

的类型ShapeSaver.classClass<ShapeSaver>。当将其提供给时genericsHell(),编译器需要检查是否Class<ShapeSaver>为的子类型Class<? extends ShapeProcessor<?>,这将简化为是否ShapeSaver为的子类型ShapeProcessor<?>。子类型关系不成立,方法调用失败。

@Bohemian的解决方案也应如此。在此,子类型检查在推断出T之后的边界检查时发生T。它也应该失败。这似乎是一个编译器错误,它以某种方式曲解了Raw可分配给的规则,就Raw<X>好像Raw是的子类型一样Raw<X>

一个简单的解决方案是声明

void genericsHell(Class<? extends ShapeProcessor> a)

确实ShapeSaver是的子类型ShapeProcessor,并且该调用会编译。

这不只是一种解决方法。有充分的理由。严格来说,对于任何Class<X>,都X必须是原始类型。例如,Class<List>可以,Class<List<String>>不能。因为确实没有代表的阶级List<string>;
只有一个类代表List

忽略将不使用原始类型的严厉警告。给定Java类型系统的设计方式,有时我们必须使用原始类型。甚至Java的核心API(Object.getClass())都使用原始类型。

您可能打算这样做

genericsHell(ShapeSaver<Circle>.class);

不幸的是,这是不允许的。Java可以但没有引入类型文字和泛型。这给很多图书馆带来了很多问题。java.lang.reflect.Type一团糟,无法使用。每个库都必须引入自己的类型系统表示法来解决该问题。

您可以向Guice借钱,例如

genericsHell( new TypeLiteral< ShapeSaver<Circle> >(){} )
                               ------------------

(学习ShaveSaver<Circle>在阅读代码时跳过胡扯)

在的方法主体中genericsHell(),您将拥有完整的类型信息,而不仅仅是类。



 类似资料:
  • 问题内容: 我有一个代表文本片段的泛型类。该文本片段可能具有多种不同模式(突出显示的不同类型)中的任何一种。这些模式用枚举表示。每个项目的Enum可能不同,但是它必须实现一个接口,该接口提供了一种将其中两个结合的方法(可以突出显示并加粗显示)。所以我有一个界面: 然后我的TextFragment是文本字符串和模式的容器。但是当我尝试声明该类时: 我收到以下错误: 令牌“扩展”的语法错误,预期 根据

  • 如何获取这个类的类型?对于上下文,我使用ModelMapper,我需要类类型T从S转换为T。 背景: 我已经尝试了N种方法,其中我放置了“//一些方法来获取类型”,但没有任何效果。例如: 或

  • 主要内容:泛型集合,泛型类,泛型方法,泛型的高级用法前面我们提到 Java 集合有个缺点,就是把一个对象“丢进”集合里之后,集合就会“忘记”这个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了 Object 类型(其运行时类型没变)。 Java 集合之所以被设计成这样,是因为集合的设计者不知道我们会用集合来保存什么类型的对象,所以他们把集合设计成能保存任何类型的对象,只要求具有很好的通用性,但这样做带来如下两个问题: 集合对元素类型没有

  • 本小节我们将学习 Java5 以后出现的一个特性:泛型(Generics)。通过本小节的学习,你将了解到什么是泛型,为什么需要泛型,如何使用泛型,如何自定义泛型,类型通配符等知识。 1. 什么是泛型 泛型不只是 Java 语言所特有的特性,泛型是程序设计语言的一种特性。允许程序员在强类型的程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须做出声明。 我们在上一小节已经了解到,Java

  • Pair泛型的实际参数类型是 Interger , 为什么不能赋值给 Number

  • 问题内容: 我想知道是否有可能编写一个接受多种通用类型的函数,如下所示: 那行得通吗?每个参数中的泛型是否意味着每个参数必须具有与泛型相同的类型T? 问题答案: 是的-可能的(虽然不是使用方法签名),是的,使用签名的类型必须相同。 使用给定的签名,必须在呼叫站点将其与单个类型(例如 或 )相关联。但是,您可以声明采用多个类型参数的方法签名 请注意,在上面的签名中,我已经声明了类型以及签名本身。因此