【问题标题】:Tricky Java Generics : generic class implementing non generic interface with generic MethodTricky Java Generics:用泛型方法实现非泛型接口的泛型类
【发布时间】:2013-04-12 19:39:19
【问题描述】:

我有以下代码as seen in ideone.com

import java.util.*;

class Test{
   interface Visitor{
        public <T> void visit(T Value);
   }

   class MyVisitor<T> implements Visitor{
        List<T> list = new  ArrayList<T>();

        public <T> void visit(T value){
           list.add(value);
        }
    }
}

编译后这段代码会产生以下+错误:

Main.java:12:错误:找不到合适的方法添加(T#1) list.add(值); ^ 方法 List.add(int,T#2) 不适用 (实际参数列表和形式参数列表的长度不同) 方法 List.add(T#2) 不适用 (实际参数 T#1 不能通过方法调用转换转换为 T#2)其中 T#1,T#2 是类型变量: T#1 扩展了在方法访问中声明的对象(T#1) T#2 扩展类 Test.MyVisitor 1 中声明的对象 1 错误

问题是 type T in visit 不被认为是相同的 T in list 。我该如何解决这个编译问题?

【问题讨论】:

    标签: java generics


    【解决方案1】:
    class MyVisitor<T> implements Visitor{
        List<T> list = new  ArrayList<T>();
    
        public <T> void visit(T value){
           list.add(value);
        }
    }
    

    等价于

    class MyVisitor<T> implements Visitor{
        List<T> list = new  ArrayList<T>();
    
        public <V> void visit(V value){
           list.add(value);
        }
    }
    

    即类的T 参数和visit 方法的T 参数不相关,也不一定可以分配给另一个。如果Visitor 本身就是一个参数化接口

    interface Visitor<V>{
        public void visit(V Value);
    }
    

    那么你可以有 MyVisitor&lt;T&gt; implements Visitor&lt;T&gt; 并且 T 的 会是一样的。

    请记住,泛型方法的重点是将两个或多个参数的类型链接起来,或者将参数的类型链接到方法的返回类型(例如,接受某种类型的参数并返回一个相同类型的列表)。当一个泛型方法只使用它的参数时,它并没有真正从泛型中获得任何好处,也就是说,您将获得同样多的类型安全性

    interface Visitor{
        public void visit(Object Value);
    }
    

    就像您在原来的 Visitor 界面中所做的那样。

    【讨论】:

    • 这也不会编译
    • @Andremoniy 这个答案用替代示例解释了 OP 在他的代码中真正做了什么,所以它当然也不会编译。
    • 如果我不能改变Visitor 接口呢?我是否必须使用原始类型 List,因为我无法匹配 T 并且删除后 T 将是 Object
    • @Ian Roberts This 说我会回到“未经检查”的警告
    • @AndréOriani 在没有警告和@SuppressWarnings 的情况下做到这一点的唯一方法是拥有一个构造函数MyVisitor(Class&lt;T&gt; cls) 并在运行时传入与@ 的类型参数相对应的Class 对象987654339@,然后在visit 中执行list.add(cls.cast(value)); 这将编译,但如果使用错误类型的参数调用visit,它仍然会在运行时失败。
    【解决方案2】:

    你声明了泛型类型&lt;T&gt; 两次:

    • 关于visit 方法
    • MyVisitor

    编译器阻止添加到列表中:list.add(value);,因为这两种类型可能不同。

    解决问题的一种方法是使&lt;T&gt;中的Visitor接口泛型,并去掉访问方法上的&lt;T&gt;

    interface Visitor<T> {
            public void visit(T Value);
        }
    
    class MyVisitor<T> implements Visitor<T>{
        List<T> list = new  ArrayList<T>();
    
        public  void visit(T value){
            list.add(value);
        }
    }
    

    【讨论】:

      【解决方案3】:

      接口必须是Visitor&lt;T&gt;

      编辑:界面必须是这样的

      interface Visitor<T> {
          void visit(T Value);
      }
      

      【讨论】:

      • 不是真的,因为泛型类型参数的阴影,这会让事情变得更加混乱
      • 认为建议是使方法在类中通用'T
      • 糟糕,忘了说您还必须摆脱泛型方法定义。
      【解决方案4】:

      正确的解决方案是:

      class Test {
          interface Visitor<T> {
              public void visit(T Value);
          }
      
          class MyVisitor<T> implements Visitor<T> {
              List<T> list = new ArrayList<T>();
      
              @Override
              public void visit(T value) {
                 list.add(value);
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        您将MyVisitor&lt;T&gt; 中的类型参数T (以及list 中的项目类型)与方法签名中的参数隐藏在一起,它可以是完全不同的类型。 (传递给visit() 的任何类型都可以是任何类型,也可能是Object。)您应该重命名其中一个。

        事实上,就 Java 泛型而言,访问者中的方法签名是毫无意义的。只需将其设为void visit(Object o),它完全等效且不那么令人困惑。这样做也使问题更清楚,您尝试将Object 添加到List&lt;T&gt;。如果您需要 Visitor 中的方法签名,则必须强制转换。 (这将需要Class.cast(),因此需要Class&lt;T&gt;MyVisitor 中的某处)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-02-19
          • 1970-01-01
          • 1970-01-01
          • 2017-11-13
          相关资源
          最近更新 更多