在基类中,我有一个实现为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>());
}
}
但它不会让我,因为它给了我一个“必须明确唤起另一个构造函数”的错误。我该怎么办?
答案 0 :(得分: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>());
}
是的,我知道这不是一个理想的解决方案。我有空的时候会修改这个。
答案 1 :(得分: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()
调用。
此外,如果你不编写自己的任何构造函数,编译器会生成一个参数less constructor,尽管你没有自动生成这样的构造函数。由于您的Base类有一个带有int的构造函数,因此当您不编写显式超级调用时,编译器会生成对super()
的调用,该调用不存在。
如果在基类中编写无参数构造函数,则代码将编译并隐式调用此代码。这是一个坏主意,因为你想让你的基类知道用户想要的大小,所以你真正想要的是调用super(size)
!
答案 2 :(得分: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>());
}
super(size)
构造函数。因为在这种情况下你想要完全覆盖构造函数,所以我将选择一个空的无参数(默认)构造函数。