【问题标题】:How do I call a method of a generic type object?如何调用泛型类型对象的方法?
【发布时间】:2020-07-16 20:09:40
【问题描述】:

下面的代码给了我错误:

SceneNode.java:17: cannot find symbol
symbol  : method execute() location:
class java.lang.Object
                operation.execute();
                         ^ 1 error

代码:

import java.util.LinkedList;
import java.util.Iterator;

public class SceneNode<T>{
    T operation;    
    public SceneNode() {
    }   
    public SceneNode(T operation) {
        this.operation = operation;
    }
    public void setOperation(T operation) {
        this.operation = operation;
    }
    public void doOperation() {
        operation.execute();
    }
}

这是一个简单场景图的缩减(为了您的可读性)开始。节点可以是模型、转换、开关等,所以我创建了一个名为operation 的变量,其类型由T 类变量定义。这样我可以传递一个Transformation / Model / Switch 对象(具有execute 方法)并像这样传递它:

SceneNode<Transformation> = new SceneNode<Transformation>(myTransformation);

我很确定拥有SceneNode 的基类和对所有各种类型的节点进行子类化会是一个更好的主意(我正在尝试泛型,最近才了解它们)。为什么这不起作用?我一定遗漏了一些关于泛型的基本知识。

【问题讨论】:

    标签: java generics


    【解决方案1】:

    它不起作用,因为T 可以是any 类型,而Java 是静态类型的。编译器不知道您是否会尝试创建 SceneNode&lt;String&gt; - 那么 execute 会做什么?

    一种选择是创建一个适当的界面,例如

    public interface Executable {
        void execute();
    }
    

    然后在SceneNode中约束T实现Executable

    public class SceneNode<T extends Executable> {
        ...
    }
    

    (我觉得 T 必须扩展 Executable 而不是在源代码中实现 它有点奇怪,但是T 可以最终成为一个接口本身,所以我想这是有道理的。)

    那么它应该可以正常工作。当然,如果您愿意,可以Executable 设为抽象超类 - 甚至是(非最终)具体类 - 但我通常更喜欢使用接口,除非我有一些理由不这样做到。

    【讨论】:

    • @dty:是的,我已经在上面了。对我来说看起来很奇怪,但这就是 Java 泛型 :(
    • 对不起,为什么不是implements Executable
    • @LukAron:因为这不是用于泛型类型边界的语法。见docs.oracle.com/javase/specs/jls/se10/html/jls-4.html#jls-4.4。我知道这有点奇怪,但它确实使它保持一致。
    【解决方案2】:

    我猜你来自 C++ 背景。

    编译器不知道 T 可能是什么类型的东西,因为你没有告诉它。

    如果您有一个名为 Executable 的接口,它定义了您的 execute() 方法,那么您需要这样做:

    public class SceneNode<T extends Executable> {
        // ... 
    }
    

    现在,编译器将知道 T 是一个Executable,并允许您访问该接口上的所有方法。

    【讨论】:

    • 因为泛型 实际上是 Java 的 Object(与 相同),直到指定了基类
    • 谢谢,这已经解决了,我现在也明白为什么了。我以前见过这种语法,但我认为它的意思是“对象必须是这个对象的子类”。事后看来,这似乎很愚蠢。
    • @Callum:意思是“参数类型必须是给定类型的子类型”,所以现在T只能使用Executable的子类型。
    • 我来自 c++,实际上也很难获得,所以感谢您的解释。不过这有点奇怪,我猜编译器应该寻找泛型类的用法以查看是否确实存在调用的方法——这就是它在 c++ 中的完成方式。必须指定从 T 扩展的类会降低泛型类的可用性,它不再是泛型的 - 它仅适用于扩展此类的对象,对吗?无论如何,我想我需要多研究一下这个话题。
    【解决方案3】:

    最近我遇到了一种情况,我必须在泛型对象上调用方法。让反思付诸行动对我有用。

    public class SceneNode<T>{
        T operation;    
        public SceneNode() {
        }   
        public SceneNode(T operation) {
            this.operation = operation;
        }
        public void setOperation(T operation) {
            this.operation = operation;
        }
        public void doOperation() {
            Method m = operation.getClass().getMethod("execute");
            m.invoke(operation);
        }
    }
    

    【讨论】:

      【解决方案4】:

      Java 是静态类型语言。您必须在编译时知道类型才能调用方法。代替子类,您可以使用定义execute() 方法的接口ExecutableT(没有任何&lt;T extends SomeClass&gt;)只有java.lang.Object定义的方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-21
        • 1970-01-01
        • 2011-02-18
        • 2012-01-11
        相关资源
        最近更新 更多