2013-05-27 35 views
0

在一个基类,我有一个变量实现为一个ArrayList:如何更改我的子类中的变量的实现?

class Base<E> { 

    List<Collection<E>> adj; 

    public Base (int size) { 
     adj = new ArrayList<Collection<E>>(size); 
     for(int i = 0; i < size; i++) 
      adj.add(new ArrayList<E>()); 
    } 
} 

现在在子类中,我想改变执行到HashSet:

class Sub<E> extends Base<E> { 

    List<Collection<E>> adj; 

    public Sub (int size) { 
     adj = new ArrayList<Collection<E>>(size); 
     for(int i = 0; i < size; i++) 
      adj.add(new HashSet<E>()); 
    } 
} 

但它不会让因为它给了我一个“必须明确地唤起另一个构造函数”的错误。我该怎么办?

回答

1

那么,如果它告诉你调用另一个构造函数,你应该尝试调用另一个构造函数。

public Sub (int size) { 
    super(size); // invoke superclass constructor 
    adj = new ArrayList<Collection<E>>(size); 
    for(int i = 0; i < size; i++) 
    adj.add(new HashSet<E>()); 
} 

是的,我知道这不是一个理想的解决方案。有空的时候我会修改这个。

+0

它初始化'adj'两次不好吗? – Duncan

+1

是的,这是不好的。两次做不好的事情表明设计不好。只需在基础中提供一个空的“受保护的”无参数构造函数。 也没有必要重新声明“adj”,只要确保它在基地0123保持受保护

+0

其实“不需要”一定是我迄今为止阅读过的最大轻描淡写。隐藏一个成员是一个可能的错误情景,我只是想着它而不知所措。特别是不必要的,因为'Collection '将会除了哈希集合就好! – Voo

0

这就是Java如何构造对象。第一件事(在一些静态初始化之后)子类的构造函数所做的是构造超类。 Java通过调用超类的默认构造函数自动执行此操作。默认的构造函数总是一个没有参数的构造函数。所以你的情况这将是:

public Base() { 
    // do construction work here 
} 

现在,因为你的超没有默认构造函数Java不知道用哪个构造函数来构造超类。

所以要么你添加一个默认的构造函数,后者又调用你的专门的构造函数,如:

public Base() { 
    this(4); // default value for example 
} 

public Base (int size) { 
    adj = new ArrayList<Collection<E>>(size); 
    for(int i = 0; i < size; i++) 
    adj.add(new ArrayList<E>()); 
} 

或者你一样@Zong李说,从内显式调用你super(size)构造你的专门的构造函数。因为在这种情况下你想完全重载构造函数,所以我会选择一个空的无参数(默认)构造函数。

+0

真的**糟糕的想法不能从给定特定大小参数的构造函数中显式调用'super(size)'。你不希望基类的大小为4,而调用者明确告诉你使用大小10.更糟糕的是,基类将假定大小为4来设置所有内容,而超级类将在大小为10时工作。 – Voo

+0

I同意这真的很糟糕。这只是一个可能性的例子。正如我的回答中所说,我更喜欢**空**默认构造函数。 –

+0

也许这只是一个“空”默认构造函数的误解,如果你的意思是不提供默认的构造函数或者如果提供一个转发'Sub(size)'到'super(size)',那么我们同意。如果你的意思只是提供一个空的Base构造函数,而不是将Sub构造函数转发给正确的基础构造函数,我们不同意,不确定。 – Voo

1

虽然有些人回答了这个问题,但他们错过了更大更重要的一点:不要永远不会影响你的基类的成员!可能有一两种罕见的情况,你有充分的理由这样做,但我想不出任何。尽管这是一个常见的初学者错误。

为了演示方便地问题:

private static class Foo { 
    protected int x = 1; 
} 

private static class Bar extends Foo { 
    protected int x = 0; 
} 

public static void main(String[] args) { 
    Bar b = new Bar(); 
    Foo f = b; 
    System.out.println(b.x); // prints 0 
    System.out.println(f.x); // prints 1 
} 

在你的榜样,你真的不希望在你的类两个清单包含不同的东西“相同”的名字。解决方案很简单:使基类中的成员受保护(否则,您只能在同一个包中进行子类化),并删除子类中的声明。

关于您的构造函数问题:这很简单,但有两个部分:如果您没有明确地在构造函数中编写任何super调用,编译器会自动将super()调用插入代码中。 此外,如果您不写任何自己的构造函数,编译器会生成一个无参数的构造函数,只要您这样做,但不会自动生成这样的构造函数。由于你的Base类有一个构造函数,它接受一个int,所以当你不写显式超级调用时,编译器会产生一个不存在的调用super()

如果你在你的基类中编写一个无参数的构造函数,你的代码将会编译并隐式调用它。这是一个坏主意,因为你想让你的基类知道用户想要的大小,所以你真正想要的是拨打super(size)

+0

比声明受保护的字段更好,应该将其设置为私有,并提供受保护的setter和getter。由于您正在使用列表,所以父类应该是填写列表的负责人。这可能不是一个简单的理由 – iberbeu

+0

@iberbeu真的,这给了你抽象等通常的优点,但我不想通过进入那些东西和内部类的时间更长的帖子,我一般很好,只是直接访问受保护的变量取决于定义将要改变的可能性 - 虽然集合列表已经非常抽象;) – Voo

相关问题