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

泛型类中的Java泛型方法

易焱
2023-03-14

如果在Java中创建泛型类(该类具有泛型类型参数),是否可以使用泛型方法(该方法采用泛型类型参数)?

考虑下面的例子:

public class MyClass {
  public <K> K doSomething(K k){
    return k;
  }
}

public class MyGenericClass<T> {
  public <K> K doSomething(K k){
    return k;
  }

  public <K> List<K> makeSingletonList(K k){
    return Collections.singletonList(k);
  }
}

正如您所期望的那样,对于任何对象,MyClass的实例,我都可以调用doSomething(K)

MyClass clazz = new MyClass();
String string = clazz.doSomething("String");
Integer integer = clazz.doSomething(1);

但是,如果我试图使用MyGenericClass的实例而不指定泛型类型,那么调用doSomething(K)返回一个对象,而不管传入了什么K

MyGenericClass untyped = new MyGenericClass();
// this doesn't compile - "Incompatible types. Required: String, Found: Object"
String string = untyped.doSomething("String");

奇怪的是,如果返回类型是泛型类,它将编译-例如List

MyGenericClass untyped = new MyGenericClass();
List<String> list = untyped.makeSingletonList("String"); // this compiles

此外,如果泛型类是类型化的,即使只使用通配符,它也会编译:

MyGenericClass<?> wildcard = new MyGenericClass();
String string = wildcard.doSomething("String"); // this compiles

>

  • 为什么在非类型化泛型类中调用泛型方法不起作用?

    是否有一些关于泛型类和泛型方法的巧妙技巧是我所缺少的?

    编辑:

    为了澄清这一点,我希望非类型化或原始类型的泛型类不遵守泛型类的类型参数(因为它们尚未提供)。然而,我不清楚为什么非类型化或原始类型的泛型类意味着不尊重泛型方法。

    发现这个问题已经在SO, c. f.这个问题上提出了。这个问题的答案解释了当一个类是非类型的/以其原始形式时,所有泛型都从类中删除——包括泛型方法的类型。

    然而,对于为什么会出现这种情况,没有真正的解释。所以请允许我澄清我的问题:

    • 为什么Java会删除非类型或原始类型泛型类上的泛型方法类型?这是有充分理由的,还是只是疏忽

    编辑-JLS的讨论:

    有人建议(在回答前一个SO问题和本问题时)在JLS 4.8中处理这一问题,其中规定:

    未从其超类或超接口继承的原始类型C的构造函数(§8.8)、实例方法(§8.4,§9.4)或非静态字段(§8.3)M的类型是对应于在对应于C的泛型声明中删除其类型的原始类型。

    我很清楚这与非类型化类有什么关系——类泛型类型被擦除类型所取代。如果类泛型被绑定,那么擦除类型对应于这些边界。如果它们没有被绑定,那么擦除类型就是Object——例如。

    // unbound class types
    public class MyGenericClass<T> {
      public T doSomething(T t) { return t; }
    }
    MyGenericClass untyped = new MyGenericClass();
    Object t = untyped.doSomething("String");
    
    // bound class types
    public class MyBoundedGenericClass<T extends Number> {
      public T doSomething(T t) { return t; }
    }
    MyBoundedGenericClass bounded = new MyBoundedGenericClass();
    Object t1 = bounded.doSomething("String"); // does not compile
    Number t2 = bounded.doSomething(1); // does compile
    

    虽然泛型方法是实例方法,但我不清楚JLS 4.8是否适用于泛型方法。泛型方法的类型(


  • 共有3个答案

    谢墨竹
    2023-03-14

    我找到了一个完全丢弃泛型的理由(但是它不太好)。原因是:泛型可能是有界的。考虑这个类:

    public static class MyGenericClass<T> {
        public <K extends T> K doSomething(K k){
            return k;
        }
    
        public <K> List<K> makeSingletonList(K k){
            return Collections.singletonList(k);
        }
    }
    

    当使用不带泛型的类时,编译器必须在doSomething中丢弃泛型。我认为所有的泛型都被丢弃了,以符合这种行为。

    makeSingletonList编译,因为Java执行从ListList的未检查强制转换

    帅令雪
    2023-03-14

    对于Java泛型,如果您使用泛型类的原始形式,那么类上的所有泛型,甚至不相关的泛型方法,如您的makeSingletonListdoWhat方法,都会变成原始的。据我所知,原因是提供与Java代码编写的前泛型的向后兼容性。

    如果泛型类型参数T,则只需将其从MyGenericClass中删除,将方法保留为泛型K。否则,您将不得不接受这样一个事实:必须将class type参数T指定给您的类,才能在类中的任何其他对象上使用泛型。

    白浩气
    2023-03-14

    向后兼容似乎是类泛型类型删除的充分理由——例如,需要它来允许您返回一个非类型化列表并将其传递给一些遗留代码。将此扩展到泛型方法似乎是一个棘手的子案例。

    4.8中的JLS代码段(您引用了它)涵盖了构造函数、实例方法和成员字段——一般来说,泛型方法只是实例方法的一个特例。看来你的案子已经被这段话涵盖了。

    使JLS 4.8适应这种特定情况:

    泛型方法的类型是原始类型,对应于C对应的泛型声明中对其类型的删除。

    (在这里,方法的“类型”将包括所有参数和返回类型)。如果你将“擦除”解释为“擦除所有泛型”,那么这似乎确实符合观察到的行为,尽管它不是很直观,甚至不是很有用。擦除所有泛型,而不仅仅是泛型类参数,这几乎看起来像是一种过度热心的一致性(尽管我是谁来猜测设计者)。

    在类泛型参数与方法泛型参数交互的情况下,可能会出现问题——在代码中,它们是完全独立的,但您可以想象其他情况下,它们被分配/混合在一起。我认为值得指出的是,根据JLS,不建议使用原始类型:

    使用原始类型只能作为对遗留代码兼容性的让步。强烈反对在Java编程语言中引入泛型后编写的代码中使用原始类型。Java编程语言的未来版本可能会禁止使用原始类型

    java开发人员的一些想法显而易见:

    http://bugs.sun.com/view_bug.do?bug_id=6400189

    (bug fix显示,为了进行类型擦除,方法的返回类型被视为方法类型的一部分)

    还有一个请求,有人似乎请求您描述的行为——只擦除类泛型参数,而不是其他泛型——但它被拒绝了,理由如下:

    请求修改类型擦除,以便在类型声明中Foo

    但如果Map

    在使用原始类型(Map)的同时仍能获得泛型(Set)的一些类型安全性,这种期望太高了

     类似资料:
    • 问题内容: 如果在Java中创建泛型类(该类具有泛型类型参数),则可以使用泛型方法(该方法带有泛型类型参数)吗? 考虑以下示例: 正如您对通用方法所期望的那样,我可以使用任何对象调用的实例: 但是,如果我尝试使用 不 指定泛型类型的实例,则无论传入什么,我都会调用返回, 奇怪的是,如果返回类型是通用类,它将编译(例如(实际上,这可以解释-参见下面的答案)): 此外,如果输入通用类,即使仅使用通配符

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

    • 编译时,此代码将产生以下+错误: 问题是访问中的类型T与列表中的T不相同。如何修复此编译问题?

    • 问题内容: 背景 我曾经写过这种方法: 应该这样称呼它: 这很好用(尽管我在研究当前容易出错的问题时在这里的答案中已经看到)。 目前的情况 无论如何,现在我正在编写以下代码(在扩展javax.servlet.jsp.tagext.TagSupport的类中): 目的是可以这样称呼: 我的评估方法中的代码显然不起作用。的第二个参数应该是Class对象。这导致我: 我的问题 如何获得通用(返回)类型的

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

    • 问题内容: 在C#中,我实际上可以这样做: 但是由于某种原因,我无法使其在Java中工作。 我要做的是在超类上创建一个静态方法,以便可以将子类转换为XML。 问题答案: 称为: 或更明确地: 更令人困惑的是,您可以拥有既构造泛型类型又具有泛型参数的构造函数。不记得该语法,也从未在愤怒中使用过它(无论如何,最好还是使用静态创建方法)。 强制转换是不安全的,并且您不能编写T.class。因此,将T.c