泛型类中的Java泛型方法

如果在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);

}

}

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

MyClass clazz = new MyClass();

String string = clazz.doSomething("String");

Integer integer = clazz.doSomething(1);

但是,如果我尝试使用MyGenericClass

指定泛型类型的实例,则无论传入什么,我都会调用doSomething(K)返回ObjectK

MyGenericClass untyped = new MyGenericClass();

// this doesn't compile - "Incompatible types. Required: String, Found: Object"

String string = untyped.doSomething("String");

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

MyGenericClass untyped = new MyGenericClass();

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

此外,如果输入通用类,即使仅使用通配符,它​​也会编译:

MyGenericClass<?> wildcard = new MyGenericClass();

String string = wildcard.doSomething("String"); // this compiles

  • 有充分的理由为什么在无类型的泛型类中调用泛型方法不起作用?

  • 是否存在一些与通用类和通用方法相关的巧妙技巧?

编辑:

为了明确起见,我希望未类型化或未类型化的泛型类不接受泛型类的类型参数(因为尚未提供)。但是,我不清楚为什么未类型化或原始类型的泛型类将意味着不遵循泛型方法。

可以看出,此问题已在SO上提出,请参见此问题。对此的答案说明,当一个类未类型化/以其原始格式时, 所有 泛型都将从该类中删除-包括泛型方法的键入。

但是,关于这种情况的原因,并没有真正的解释。因此,请允许我澄清我的问题:

  • Java为什么要删除无类型或原始类型的通用类上的通用方法类型?是否有充分的理由,还是仅仅是疏忽?

编辑-JLS的讨论:

已经建议(作为对先前的SO问题和对此问题的回答)在JLS

4.8中对此进行了处理,其中指出:

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

对我来说,这很明显与无类型的类有什么关系-类的泛型被替换为擦除类型。如果类的泛型是绑定的,则擦除类型对应于那些边界。如果它们未绑定,则擦除类型为对象-例如

// 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是否适用于通用方法。通用方法的类型(<K>在前面的示例中)不是未类型化的,因​​为它的类型是由方法参数确定的-只有类是未类型化/原始类型的。

回答:

“为了向后兼容”似乎是擦除类泛型类型的充分理由-

需要这样做,例如,允许您返回无类型的List并将其传递给某些旧代码。将此扩展到泛型方法似乎是一个棘手的子案例。

4.8(您引用)中的JLS片段涵盖了构造函数,实例方法和成员字段-泛型方法通常只是实例方法的一种特殊情况。因此,似乎您的案例已被此代码段覆盖。

使JLS 4.8适应此特定情况:

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

(此处方法的“类型”将包括所有参数和返回类型)。如果您将“擦除”解释为“擦除所有泛型”,那么这似乎与观察到的行为相符,尽管它不是很直观,甚至没有用。删除所有泛型,而不仅仅是泛型类参数,似乎几乎是过于狂热的一致性(尽管我是第二位猜测设计师的人)。

类通用参数与方法通用参数交互时可能会出现问题-

在代码中它们是完全独立的,但是您可以想象其他情况下将它们分配/混合在一起的情况。我认为值得指出的是,根据JLS,不建议使用原始类型:

仅允许使用原始类型作为对遗留代码兼容性的让步。强烈建议不要在将通用性引入Java编程语言后在代码中使用原始类型。Java编程语言的未来版本可能会禁止使用原始类型

Java开发人员的一些想法在这里显而易见:

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

(bug +修复表明,为了消除此类型,将方法的返回类型视为方法的类型的一部分)

还有一个请求,其中有人似乎在请求您描述的行为-

仅删除类的泛型参数,而不是其他泛型-但由于以下原因而被拒绝:

要求修改类型擦除,以便在类型声明中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>)的类型安全性,这实在太多了。

以上是 泛型类中的Java泛型方法 的全部内容, 来源链接: utcz.com/qa/435697.html

回到顶部