泛型类中的 Java 泛型方法 [英] Java generic methods in generics classes

查看:34
本文介绍了泛型类中的 Java 泛型方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果你在Java中创建了一个泛型类(该类有泛型类型参数),你能不能使用泛型方法(该方法接受泛型类型参数)?

If you create a generic class in Java (the class has generic type parameters), can you use generic methods (the method takes generic type parameters)?

考虑以下示例:

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):

As you would expect with a generic method, I can call doSomething(K) on instances of MyClass with any object:

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

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

However, if I try to use instances of MyGenericClass without specifying a generic type, I calling doSomething(K) returns an Object, regardless of what K was passed in:

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

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

Oddly, it will compile if the return type is a generic class - e.g. List<K> (Actually, this can be explained - see answer below):

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

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

Also, it will compile if the generic class is typed, even if only with wildcards:

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

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

    • Is there a good reason why calling a generic method in an untyped generic class shouldn't work?

      是否有一些我遗漏的与泛型类和泛型方法相关的巧妙技巧?

      Is there some clever trick relating to generic classes and generic methods that I am missing?

      澄清一下,我希望无类型或原始类型的泛型类不接受泛型类的类型参数(因为它们尚未提供).但是,我不清楚为什么无类型或原始类型的泛型类意味着不尊重泛型方法.

      To clarify, I would expect an untyped or raw-typed generic class not to honour the generic class's type parameters (because they haven't been provided). However, it's not clear to my why an untyped or raw-typed generic class would mean that generic methods are not honoured.

      事实证明这个问题已经在 SO 上提出,c.f.这个问题.这个问题的答案解释了当一个类没有类型化/原始形式时,所有泛型都从类中删除 - 包括泛型方法的类型化.

      It transpires that this issue has already been raised on SO, c.f. this question. The answers to this explain that when a class is untyped / in its raw-form, all generics are removed from the class - including typing of generic methods.

      然而,对于为什么会这样,并没有真正的解释.所以请允许我澄清我的问题:

      However, there isn't really an explanation as to why this is the case. So allow me to clarify my question:

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

      编辑 - 对 JLS 的讨论:

      EDIT - discussion of JLS:

      有人建议(在回答上一个 SO 问题和这个问题时)在 JLS 4.8,其中指出:

      It has been suggested (in answer to the previous SO question and to this question) that this is treated in JLS 4.8, which states:

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

      The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.

      我很清楚这与无类型类的关系 - 类泛型类型被擦除类型替换.如果类泛型被绑定,那么擦除类型对应于这些边界.如果它们未绑定,则擦除类型为 Object - 例如

      It is clear to me how this relates to an untyped class - the class generic types are replaced with the erasure types. If the class generics are bound, then the erasure type corresponds to those bounds. If the they are not bound, then the erasure type is Object - e.g.

      // 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 是否适用于泛型方法.泛型方法的类型(前面示例中的 )不是无类型的,因为它的类型是由方法参数决定的 - 只有类是无类型/原始类型的.

      Whilst generic methods are instance methods, it is not clear to me that JLS 4.8 applies to generic methods. The generic method's type (<K> in earlier example) is not untyped, as it's type is determined by the method parameters - only the class is untyped / raw-typed.

      推荐答案

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

      'for backwards compatibility' seems a sufficient reason for the type erasure of class generic types - it is needed e.g. to allow you to return an untyped List and pass it to some legacy code. The extension of this to generic methods seems like a tricky sub-case.

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

      The JLS snippet from 4.8 (which you quote) covers constructors, instance methods and member fields - generic methods are just a particular case of instance methods in general. So it seems your case is covered by this snippet.

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

      Adapting JLS 4.8 to this specific case :

      泛型方法的类型是对应于在对应于 C 的泛型声明中擦除其类型.

      The type of a generic method is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.

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

      (here the 'type' of the method would include all parameter and return types). If you interpret 'erasure' as 'erasing all generics', then this does seem to match the observed behaviour, although it is not very intuitive or even useful. It almost seems like an overzealous consistency, to erase all generics, rather than just generic class parameters (although who am I to second guess the designers).

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

      Perhaps there could be problems where the class generic parameters interact with the method generic parameters - in your code they are fully independent, but you could imagine other cases where they are assigned / mixed together. I think it's worth pointing out that use of raw types are not recommended, as per the JLS :

      只允许使用原始类型作为兼容性的让步遗留代码.在编写的代码中使用原始类型将泛型引入 Java 编程语言是强烈劝阻.Java 的未来版本可能编程语言将禁止使用原始类型

      The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of genericity into the Java programming language is strongly discouraged. It is possible that future versions of the Java programming language will disallow the use of raw types

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

      Some of the thinking of the java developers is apparent here :

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

      (错误 + 修复显示方法的返回类型被视为方法类型的一部分,以便进行类型擦除)

      (bug + fix showing that a method's return type is treated as part of the method's type for the purposes of this type erasure)

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

      There is also this request, where someone appears to request the behaviour you describe - only erase the class generic parameters, not other generics - but it was rejected with this reasoning:

      请求是修改类型擦除,以便在类型声明Foo中,擦除只从参数化类型中删除T.然后,碰巧在 Map 的声明中,Set> 擦除为 Set<;Map.Entry>.

      The request is to modify type erasure so that in the type declaration Foo<T>, erasure only removes T from parameterized types. Then, it so happens that within Map<K,V>'s declaration, Set<Map.Entry<K,V>> erases to Set<Map.Entry>.

      但是如果 Map 有一个采用 Map 类型的方法,它的擦除将是 Map.对于类型擦除更改类型参数的数量是可怕的,尤其是对于编译时方法解析.我们绝对不会接受这个请求.

      But if Map<K,V> had a method that took type Map<String,V>, its erasure would just be Map<String>. For type erasure to change the number of type parameters is horrific, especially for compile-time method resolution. We are absolutely not going to accept this request.

      期望能够使用原始类型(Map)同时仍然获得泛型的一些类型安全性(Set)>).

      It is too much to expect to be able to use raw types (Map) while still getting some of the type-safety of generics (Set<Map.Entry>).

      这篇关于泛型类中的 Java 泛型方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆