【问题标题】:Inherit from any subclass of a superclass (Java)从超类的任何子类继承 (Java)
【发布时间】:2012-09-10 05:46:27
【问题描述】:

我是一名 C++ 程序员,在 C++ 中我可以做这样的事情:

class SuperSuper
{
    virtual void newItem()=0;
}

class SubSuperA:public SuperSuper
{
     virtual void newItem()
     {
         //do something
     }
}

class SubSuperB:public SuperSuper
{
     virtual void newItem()
     {
         //do something different (disappear! :)
     }
}

template <class T>
class SubSub:public T
{
    virtual void newItem()
    {
         T::newItem();
         //do a third thing
    }
}

我想在 Java 中做到这一点,但是我感觉它(至少直接)是不可能的

我在 Java 中轻松设置了前三个类,如下所示:

public abstract class SuperSuper
{
    abstract void newItem(Item item);
}

public class SubSuperA extends SuperSuper
{
    @Override void newItem(Item item)
    {
        //stuff
    }
}

public class SubSuperB extends SuperSuper
{
    @Override void newItem(Item item)
    {
        //other stuff
    }
}
//SubSuperC,D,etc

目前正在像这样实施第四个:

SubSuperA sa=new SubSuperA() {
    @Override public void newItem(Item item)
    {
        super.newItem(item);
        //A lot of stuff that will be changing constantly throughout development
    }
};

SubSuperB sb=new SubSuperB() {
    @Override public void newItem(Item item)
    {
        super.newItem(item);
        //A lot of stuff that will be changing constantly throughout development
    }
};

请注意,这两个覆盖具有相同的功能。无论它基于什么 SubSuper*,替换 newItem() 都是相同的。显然,这将是一场噩梦。我希望我可以像这样声明一个 SubSub 类

 public class SubSub<T> extends T {
    @Override public void newItem(Item item)
    {
        super.newItem(item);
        //A lot of stuff that will be changing constantly throughout development
    }
 }

我可以像这样实例化

 SubSub ss=new SubSub<SubSuperA>

等等...但是java中的这个错误:

"Cannot refer to the type parameter T as a supertype."

我根据该错误环顾四周,发现 Extend from Generic Supertype? 声称使用该方法无法实现我想要做的事情。

我真正需要做的:我有一个与平台无关的推特客户端,它只处理接收推文、存储推文等。它有一个抽象的列类 (SuperSuper),我可以为各种不同类型的列 (SubSuper) 扩展它(主页时间线、提及等),并且每个都过滤掉他们想要在 newItem() 中显示的推文。我希望能够为不同的平台插入各种 GUI,并且我需要客户端调用 GUI 特定函数 (SubSub) 来处理将每个单独的推文添加到 GUI 库。拥有一个可以扩展任何 Column 派生类的 GUI 特定类将是理想的解决方案

由于显然这种直接实现在 Java 中是不可能的,我想知道是否有一些语法技巧可以实现相当相似的东西,或者我是否应该创建一个接口,该接口将由 SuperSuper 存储和调用SuperSub* 并由我的 GUI 处理程序而不是 SubSub 实现。

【问题讨论】:

  • 那么,你有一个类 A,类 M 和 P 派生自(扩展)A,有什么转折? (只要将A类定义为abstract就可以将A中的方法定义为abstract,这将需要非抽象的M和P类来实现该方法)
  • 我可以很好地定义 M 和 P。问题是我想制作可以在实例化时扩展 A、M 或 P 的 SubSub。在我的 java 实现中,它已经定义了 A、M 和 P 并且正在工作,包括所有抽象的东西
  • 嗯,我很确定你不能这样做——你能多谈谈你的用例吗?例如,如果您只想保证 A、M、P 和 Z(您的“SubSub”)都可以实现一个通用接口,这将保证定义的规范方法可用。
  • M,P 和未知数量的其他类扩展 A(或实现 A 等,我很灵活),我希望能够编写 Z(它覆盖 A 中的一个函数(因此是 MP))(或只是 newItem())一次,然后当我执行 new M() 或 new P() 时能够覆盖 MP 中的 newItem
  • 我希望您可以用更具体的内容更新您的问题。您所说的抽象继承不太可能是不可能的。您不能在运行时扩展任意类(= Java 中的实例化)。您可以通过称为动态代理的东西为接口提供运行时实现(包括将多个接口拼接在一起,但不会有层次关系)。

标签: java generics inheritance multiple-inheritance


【解决方案1】:

模板可以使用 Java 的泛型。然而,在 Java 中,接口远比继承更流行。这是因为 Java 不支持多重继承。

在 C++ 中,您可以扩展多个类。

class X : public A, public B, private C { }

因为 C++ 不区分虚拟和非虚拟,只区分虚拟和非虚拟成员,所以类之间没有正式的区别,可以或可能没有虚拟方法。

另一方面,Java 具有三种不同的类类型:

  • 具体(完全非虚拟,所有方法都是具体的)
  • 抽象(部分虚拟,部分非虚拟)
  • 接口(完全虚拟,没有具体方法)

在 Java 中,虚拟方法被称为抽象方法,所以我将在这里称它们为抽象方法。

Java 也有泛型。泛型在语法上类似于模板,但在实现方面完全不同。我将把实施研究留给您(在您的搜索中使用的关键字是“擦除”)。

泛型在类上声明。例如,Java 等效于 C++ 的vectorArrayList 类。它的用途非常相似:

List<String> strs = new ArrayList<String>();

这里,ListArrayList 的一种超类型。 List 是一个接口,ArrayList 实现了它。 List 本身在其声明中声明了一个泛型类型,如下所示:

public interface List<E> ... {

    // ...

    E get(int index);

    // ...
}

注意get 如何返回E。在这里,E 没有具体定义。它是一个泛型类型,在这种情况下,任何东西都可以用来代替它,StringObjectBoolean、你自己的类等等。

ArrayList 实现了List,并且还声明了一个泛型类型:

public ArrayList<E> implements List<E>, ... {

    // ...

    public E get(int index) {
        // bounds check
        return elements[i];
    }

    // ...
}

您还可以使用类型边界来限制泛型的类型。这有点超出了这个范围,因为你想要的不是真正的泛型。你需要接口。

在您的示例中,您将首先使用newItem 声明一个接口。

public interface A {
    void newItem();
}

每个方法都必须来自具体的实现或接口,因此需要这个接口。具体来说,这个接口相当于SuperSuper。它是一个完全虚拟的类(接口)。现在上课:

public class B implements A {
    @Override
    public void newItem() {
        // Do something
    }
}

public class C implements A {
    @Override
    public void newItem() {
        // Do something else
    }
}

对于像我这样没有太多 C++ 经验的人来说,您的最后一点有点令人困惑,但我假设您所说的是您想要使用 newItem 方法创建模板类型。在 Java 中,这将是 A 接口的另一个实现,或者您可以扩展 B 或 C 并覆盖它们的方法。

public class D extends B {
    @Override
    public void newItem() {
        // And yet another something else
    }
}

然后使用 newItem 方法很简单:

A a = new B(); // or "new C()" or "new D()"
a.newItem();

就像你说的那样

SuperSuper* a = new SubSuperA();

在 Java 中,您可以对接口、抽象类和具体类做同样的事情,就像我在上面所做的那样。

所以我认为接口是解决您问题的方法。我强烈建议您阅读有关如何在 Java 中使用接口的内容。它们在没有多重继承的情况下提供了强大的类型。我会很快找到几个链接。

希望这些帮助。如果您需要任何说明,请告诉我。

【讨论】:

  • 谢谢,这很有帮助。问题是我需要为 A 的每个子类(B、C、Q、H 等)定义 D 多次,即使只有一个相同版本的 D.newItem() 他们都会使用(而且我需要更新/同步!)有没有办法只声明一次 D.newItem() 并覆盖所有 A 子类中的版本?
  • @zacaj 听起来您可能想尝试不同的设计方法。由于 Java 不支持多重继承,因此您不能扩展一个以上的类,这对于您正在尝试的内容是必需的。也许你应该看看策略模式(link 1link 2)。这允许您在运行时切换实现。
  • 这不是多重继承。 D 的每个实例只能从一个子类继承
  • 但我的观点是,每个 D 都将从不同的类继承,这意味着对于每个 B、C 等,您都需要一个 D 类。我并不是说多重继承本身,因为我的意思是 D 不能同时或什至在不同时间成为 B 和 C 的实例。 (尽管它们都可以是 A 的实例,这就是为什么我认为接口是一个合理的解决方案,如果这有意义的话)
【解决方案2】:

你不能。

C++ 模板是真正的源代码“模板”。当您有一个模板类或函数时,对于用作类型参数的每种类型,编译器都会有效地生成(“实例化”)源代码的单独副本,其中类型参数替换为实际类型参数。例如,vector&lt;int&gt;vector&lt;string&gt; 是完全独立的类型,它们的函数的编译代码也可能是独立的(就像它们是独立的类型一样)。

这就是为什么可以做一些事情,比如从类型参数继承。对于编译器,它只是用任何类型的参数替换T 在它看到的任何地方。生成的代码是有效的,所以它可以工作。

Java 中的泛型非常不同。泛型类或方法只有一份编译后的代码副本。该一个版本的编译代码必须同时适用于其范围内的任何可能的类型参数(现有的或未来的)。

所以想想如果你能按照你的建议去做这意味着什么。 SubSub 类的编译代码只有一份副本,它继承了,嗯,T,不管它是什么。所以它必须同时继承每个类型。这怎么可能?一个类只能有一个明确的父类。

【讨论】:

    【解决方案3】:

    根据您的 Java 代码,我认为您确实需要装饰器模式:

    public class SubSub<T extends SuperSuper> extends SuperSuper {
    
         private final Class<T> clazz;
    
         public SubSub(Class<T> clazz){
             this.clazz = clazz;
         }
    
         @Override
         public void newItem(Item item){
              T sup = clazz.newInstance();
              sup.newItem(item);
              // other stuff
         }
    }
    

    使用如下调用:

    SubSub<SubSuperA> foo = new SubSub<SubSuperA>(SubSuperA.class);
    

    如果您只想将类的名称作为参数传递,则可以完全摆脱泛型:

    public class SubSub extends SuperSuper {
    
         private final SuperSuper _super;
    
         public <T extends SuperSuper> SubSub(Class<T> clazz){
             _super = clazz.newInstance();
         }
    
         @Override
         public void newItem(Item item){
              _super.newItem(item);
              sup.newItem(item);
              // other stuff
         }
    }
    

    使用形式的实例化:

    SubSub foo = new SubSub(SubSuperA.class);
    

    或:

    SubSub foo = new SubSub((Class<SuperSuper>) Class.forName("SubSuperA"));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-14
      • 1970-01-01
      • 1970-01-01
      • 2023-03-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多