4

我是一名 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."

我根据该错误环顾四周,发现从通用超类型扩展?它声称使用该方法无法实现我想要做的事情。

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

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

4

3 回答 3

2

模板可以使用 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>();

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

public interface List<E> ... {

    // ...

    E get(int index);

    // ...
}

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

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 中使用接口的内容。它们在没有多重继承的情况下提供了强大的类型。我会很快找到几个链接。

希望那些帮助。如果您需要任何澄清,请告诉我。

于 2012-09-18T02:13:11.230 回答
1

你不能。

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

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

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

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

于 2012-09-25T05:23:06.860 回答
0

根据您的 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"));
于 2012-09-18T03:15:44.017 回答