【问题标题】:use implementation type in interface in java在java的接口中使用实现类型
【发布时间】:2012-08-27 07:27:07
【问题描述】:

我想为实现的类的类型创建一个接口,强制实现它的每个类具有特定的功能。

假设我有 MyClassA、MyClassB、MyClassC 等类,它们都需要针对自己类型的函数:

在 MyClassA 中:

public class MyClassA implements MyClass {
    MyClassA function(MyClassA x) {
        doSomethingImplementedInMyClassA(x);
    }
}

在 MyClassB 中:

public class MyClassB implements MyClass {
    MyClassB function(MyClassB x) {
        doSomethingImplementedInMyClassB(x);
    }
}

问题是,接口MyClass需要这样的功能怎么写?

public interface MyClass {
    MyClass function(MyClass x);
}

显然不起作用,因为返回类型是 MyClass 而不是它的实现。如何在 Java 中正确执行此操作?

【问题讨论】:

  • 有什么原因不能在界面中添加doSomethingImplementedInMyClass()
  • 我不确定这会有什么帮助?
  • 你想通过创建一个“强制实现它的每个类具有特定功能的接口,用于实现类的类型”来实现什么?您尝试如何解决问题可能是不可能的,但应该存在替代方案。
  • 我正在尝试制作几种新类型的数字(如果你知道一些数学,实际上是“组元素”),我想实现 a.multiplyBy(b);和类似的方法。所以我会创建一个接口GroupElement 并要求GroupElement 的每个实现将乘法例程定义为ImplementationOfGroupElement multiplyBy(ImplementationOfGroupElement b) {...}

标签: java generics interface


【解决方案1】:

你可以使用泛型:

public interface MyClass<V extends MyClass<V>> {
    V function(V x);
}

public class MyClassA implements MyClass<MyClassA> 

这称为CRTP


这并不完美;它仍然允许像

public class MyClassB implements MyClass<MyClassA> 

要正确执行此操作,您需要 Java 不支持的更高种类的类型[需要引用]

【讨论】:

  • 啊哈,使用一个类作为它自己的泛型,这很聪明!谢谢!
  • @user1111929:顺便说一句,你的意思是“作为它自己的通用参数”。
  • 这是一个 C++ 习语,所以它与问题完全无关,Java 有这方面的功能。检查我的答案。
【解决方案2】:

如果实现总是调用参数上的方法,为什么不直接将该方法添加到接口中?

interface MyClass {
    MyClass doSomething();
}

class MyClassA implements MyClass {
    MyClassA doSomething() {
        //implementation here
    }
}

class MyClassB implements MyClass {
    MyClassB doSomething() {
        //implementation here
    }
}

【讨论】:

    【解决方案3】:

    除了接口之外,您可能还需要一个父类。

    我发现一个接口最适合用来定义行为,因此GroupElementA 是一个 GroupElement 在概念上听起来比GroupElementA 更准确 表现得像一个 @987654324 @。

    我会考虑使用父类来实现你想要的。

    /**
     * An abstract group element.
     */
    abstract class GroupElement
    {
        // attributes of all group elements
    }
    
    /**
     * Defines behavior for objects that can be multiplied with
     * GroupElements.
     */
    interface GroupElementMultipliable
    {
        public GroupElement multiplyBy(GroupElement groupElement);
    }
    
    /**
     * Defines behavior for objects that can be divided by
     * GroupElements.
     */
    interface GroupElementDivisible
    {
        public GroupElement divideBy(GroupElement groupElement);
    }
    
    /**
     * An abstract GroupElement that can perform operations like
     * multiplication and division.
     *
     * Then again this class may not be necessary. The interfaces
     * implemented here may actually be directly implemented by
     * GroupElementA. GroupElementA will also be the one to inherit
     * GroupElement.
     */
    abstract class OperableGroupElement extends GroupElement
            implements GroupElementMultipliable, GroupElementDivisible
    {
        // attributes of all operable group elements
    }
    
    /**
     * A concrete GroupElement that can perform operations like
     * multiplication and division.
     */
    class GroupElementA extends OperableGroupElement
    {
        @Override
        public GroupElementA multiplyBy(GroupElement groupElement)
        {
            // Since we expect to multiply with another GroupElementA
            // we attempt to typcast the groupElement
            GroupElementA groupElementA = (GroupElementA) groupElement;
    
            // do multiplication operation -- this * groupElementA
            // then return new self
            return this;
        }
    
        @Override
        public GroupElementA divideBy(GroupElement groupElement)
        {
            // Since we expect to divide by another GroupElementA
            // we attempt to typcast the groupElement
            GroupElementA groupElementA = (GroupElementA) groupElement;
    
            // do division operation -- this / groupElementA
            // then return new self
            return this;
        }
    }
    

    【讨论】:

    • 这是一个有趣的方法。但是,如果需要类型转换,那么它是否仍然比 SLaks 建议的泛型参数方法更快?它当然不会产生更短的代码。你的方法到底有什么好处?我倾向于尽可能使用接口,因为它们不会降低继承速度(即使是抽象类也会降低,iirc)。
    • 嗨@user1111929,很高兴你发现这很有趣。实际上,这也是我第一次了解 CRTP(感谢 SLaks),所以我无法真正比​​较我的方法。是的,它确实占用了更多的 LOC,但它是一个很好的 OO 解决方案,AFAIK。我并不是一个真正的性能迷,但我也阅读过有关性能的文章,唯一的主要问题似乎是类型转换。
    【解决方案4】:

    在 Java 中做你想做的事情的正确方法是这样的:

    首先定义包含一个抽象方法的接口,这是一个尚未定义的方法。 提醒一下,接口不是类!

    public interface Animal {
        public Animal reproduceWith(Animal someAnimal);
    }
    

    然后您定义实现接口的类并覆盖抽象方法,但现在您使用您选择的代码实现它。这样,类中唯一相同的就是方法名称。 这有效地强制类实现某个方法。

    public class Dog implements Animal {
    
        @Override
        public Animal reproduceWith(Animal someAnimal) {
            return new Dog();
        }
    }
    
    
    public class Cat implements Animal {
    
        @Override
        public Animal reproduceWith(Animal someAnimal) {
            return new Cat();
        }
    }
    

    之后,例如,您可以创建一个 MyInterface 列表并对其进行迭代,调用相同的方法,即使它是不同的类。

    List<Animal> list = new ArrayList<Animal>();
    list.add(new Cat());
    list.add(new Dog());
    
    Animal cat = new Cat();
    Animal dog = new Dog();
    
    for (Animal animal : list) {
        System.out.println(animal.reproduceWith(cat));
        System.out.println(animal.reproduceWith(dog));
    }
    

    希望对你有所帮助。

    【讨论】:

    • 我真的不明白这与问题有什么关系...我应该怎么做才能在您的代码中获得a.doSomething(b); 的效果?
    • public MyInterface myAbstractMethod();在接口和实现它的类中。我将编辑答案。
    • 我仍然不明白 a.doSomething(b); 的效果如何。您的代码中的 ab 是什么?
    • a 是 MyClassA,b 是 MyClassB。所以你可以调用接口的方法并给它一个接口作为参数,但类可以不同。诀窍是您知道该方法是相同的,因此您可以调用它而无需检查类型。我编辑了我的答案以使其更清晰。
    • 不客气,但这为您提供了同样的可能性;) 基本上,在这种情况下,您的泛型类型就是接口本身。 MyInterface a = new MyClassA(); MyInterface b = new MyClassB(); a.doSomething(b);
    猜你喜欢
    • 1970-01-01
    • 2012-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-06
    • 2021-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多