首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >泛型类中的Java泛型方法

泛型类中的Java泛型方法
EN

Stack Overflow用户
提问于 2013-08-01 18:15:31
回答 6查看 10.3K关注 0票数 31

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

请考虑以下示例:

代码语言:javascript
复制
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实例调用MyClass

代码语言:javascript
复制
MyClass clazz = new MyClass();
String string = clazz.doSomething("String");
Integer integer = clazz.doSomething(1);

但是,如果我尝试使用MyGenericClass实例而不指定泛型类型,则调用doSomething(K)将返回一个Object,而不管传入的是什么K

代码语言:javascript
复制
MyGenericClass untyped = new MyGenericClass();
// this doesn't compile - "Incompatible types. Required: String, Found: Object"
String string = untyped.doSomething("String");

奇怪的是,如果返回类型是泛型类,那么它将编译--例如List<K> (实际上,这可以解释--参见下面的答案):

代码语言:javascript
复制
MyGenericClass untyped = new MyGenericClass();
List<String> list = untyped.makeSingletonList("String"); // this compiles

此外,如果输入了泛型类,即使只使用通配符,它也会编译:

代码语言:javascript
复制
MyGenericClass<?> wildcard = new MyGenericClass();
String string = wildcard.doSomething("String"); // this compiles
  • 为什么在非类型化泛型类中调用泛型方法不应该工作,有充分的理由吗?
  • 有什么关于泛型类和泛型方法的巧妙技巧吗?

编辑:

为了澄清,我希望一个非类型化或原始类型化的泛型类不会遵守泛型类的类型参数(因为它们还没有被提供)。但是,我不清楚为什么非类型化或原始类型化泛型类意味着泛型方法不受尊重。

据了解,这一问题已经提出,关于这样,c.f。这个问题。这个问题的答案解释了,当一个类是非类型化/其原始形式时,所有的泛型都会从类中移除--包括泛型方法的类型。

然而,没有一个真正的解释为什么是这样的。因此,请允许我澄清我的问题:

  • 为什么Java要删除非类型化或原始类型泛型类上的泛型方法类型?这有什么好的理由吗,还是只是个疏忽?

编辑-JLS的讨论:

有人建议(对前一个这样的问题和这个问题的答复)在JLS 4.8中处理这一问题,其中规定:

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

在我看来,这与非类型化类有什么关系--类泛型类型被擦除类型所取代。如果类泛型是绑定的,则擦除类型对应于这些界限。如果它们没有绑定,那么擦除类型就是对象。

代码语言:javascript
复制
// 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

虽然泛型方法是实例方法,但我不清楚JLS4.8是否适用于泛型方法。泛型方法的类型(在前面的示例中是<K>)不是非类型化的,因为它的类型是由方法参数决定的--只有类是非类型化/raw类型的。

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2013-08-06 14:17:48

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

4.8 (引用的) JLS片段涵盖了构造函数、实例方法和成员字段--泛型方法只是实例方法的一个特殊情况。看来你的案子被这段片段覆盖了。

根据这一具体情况调整JLS 4.8:

泛型方法的类型是与其类型在与C对应的泛型声明中擦除的原始类型。

(在这里,方法的“类型”将包括所有参数和返回类型)。如果您将“擦除”解释为“擦除所有泛型”,那么这似乎与观察到的行为相匹配,尽管它并不是非常直观甚至有用的。擦除所有的泛型,而不仅仅是泛型类参数(虽然我是谁来猜测设计者),这似乎是一种过于热心的一致性。

可能存在类泛型参数与方法泛型参数交互的问题--在您的代码中它们完全独立,但您可以想象其他将它们赋值/混合在一起的情况。我认为值得指出的是,根据JLS,不推荐使用原始类型:

只允许使用原始类型作为对遗留代码兼容性的让步。在Java编程语言中引入泛型之后编写的代码中使用原始类型是非常不可取的。Java编程语言的未来版本有可能不允许使用原始类型

java开发人员的一些想法在这里很明显:

id=6400189

(bug + fix显示方法的返回类型被视为方法类型的一部分,用于此类型的擦除)

还有这一请求,有人似乎请求您描述的行为--只删除类的泛型参数,而不是其他泛型--但基于这种推理,它被拒绝了:

请求修改类型擦除,以便在类型声明Foo<T>中,擦除只从参数化类型中删除T。然后,在Map<K,V>的声明中,Set<Map.Entry<K,V>>擦除了Set<Map.Entry>。 但是,如果Map<K,V>有一个采用Map<String,V>类型的方法,那么它的擦除将只是Map<String>。对于类型擦除,更改类型参数的数量是可怕的,特别是对于编译时方法解析。我们绝对不会接受这项要求。 希望能够在获得泛型(Map)的某些类型安全性(Set<Map.Entry>)的同时使用原始类型( raw Type)是太过分了。

票数 8
EN

Stack Overflow用户

发布于 2013-08-01 18:20:43

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

如果泛型类型参数T没有用,那么只需从MyGenericClass中删除它,将方法保留为K。否则,您将不得不接受这样一个事实:类类型参数T必须给您的类使用泛型,以便在类中的任何其他内容上使用泛型。

票数 4
EN

Stack Overflow用户

发布于 2013-08-06 11:48:42

我找到了一个完全抛弃泛型的理由(然而,这并不是很好)。原因是:泛型可能有界。考虑一下这门课:

代码语言:javascript
复制
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编译是因为List进行了从ListList<K>的未检查转换(但是编译器会显示警告)。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18001550

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档