Java 如何在不重复代码的情况下编写子类构造函数?
我想在抽象超类的构造函数中调用一个抽象方法,Java 如何在不重复代码的情况下编写子类构造函数?,java,oop,inheritance,Java,Oop,Inheritance,我想在抽象超类的构造函数中调用一个抽象方法,generateId(),这个抽象方法依赖于相应子类的一些字段。考虑下面的代码清晰: 抽象类:超类 public abstract class SuperClass { protected String id; public SuperClass() { generateId(); } protected abstract void generateId(); } 子类:Sub1 public class
generateId()
,这个抽象方法依赖于相应子类的一些字段。考虑下面的代码清晰:
抽象类:超类
public abstract class SuperClass {
protected String id;
public SuperClass() {
generateId();
}
protected abstract void generateId();
}
子类:Sub1
public class Sub1 extends SuperClass {
private SomeType fieldSub1;
public Sub1(SomeType fieldSub1) {
this.fieldSub1 = fieldSub1;
super();
}
protected void generateId() {
// Some operations that use fieldSub1
}
}
子类:Sub2
public class Sub2 extends SuperClass {
private SomeOtherType fieldSub2;
public Sub2(SomeOtherType fieldSub2) {
this.fieldSub2 = fieldSub2;
super();
}
protected void generateId() {
// Some operations that use fieldSub2
}
}
但是,由于super()
必须是构造函数中的第一条语句
OTOH,如果我做super()
子类构造函数中的第一条语句,那么我将无法在超类中调用generateId()。因为generateId()
使用子类中的字段,这些字段在使用之前必须初始化
在我看来,“解决”这个问题的唯一方法是:删除超类中对generateId()
的调用。在每个子类的构造函数末尾调用generateId()
。但这会导致代码重复
那么,有没有办法在不重复我的代码的情况下解决这个问题呢?(也就是说,无需在每个子类的构造函数末尾调用generateId()
)您可以尝试使用实例或静态初始化块
初始化顺序:
按源代码顺序排列的所有静态项
所有成员变量
调用构造函数
例如:
class InitBlocksDemo {
private String name ;
InitBlocksDemo(int x) {
System.out.println("In 1 argument constructor, name = " + this.name);
}
InitBlocksDemo() {
name = "prasad";
System.out.println("In no argument constructor, name = " + this.name);
}
/* First static initialization block */
static {
System.out.println("In first static init block ");
}
/* First instance initialization block */
{
System.out.println("In first instance init block, name = " + this.name);
}
/* Second instance initialization block */
{
System.out.println("In second instance init block, name = " + this.name);
}
/* Second static initialization block */
static {
System.out.println("In second static int block ");
}
public static void main(String args[]) {
new InitBlocksDemo();
new InitBlocksDemo();
new InitBlocksDemo(7);
}
}
输出:
In first static init block
In second static int block
In first instance init block, name = null
In second instance init block, name = null
In no argument constructor, name = prasad
In first instance init block, name = null
In second instance init block, name = null
In no argument constructor, name = prasad
In first instance init block, name = null
In second instance init block, name = null
In 1 argument constructor, name = null
示例来源:。您可以使超类
具有一个ParentType
变量,然后在子类中强制转换该类型。很抱歉我在评论中说的话,术语有点不恰当。不能在子类中更改超类变量的类型。但是你可以投
我刚刚意识到不能传递一个超类并将其转换为子类。你只能走另一条路
无论如何,我想到了另一个可能的解决办法。首先是变量:
public class ParentType { //stuff }
public class SomeType extends ParentType { //more stuff }
public class SomeOtherType extends ParentType { //even more stuff }
你可以用你的想法。顺便说一句,我在这里冒险进入未知领域,我以前从未这样做过。如果有人发现此错误,请评论或编辑
使您的类成为通用类:
public abstract class SuperClass<Type extends ParentType> {
protected Type field;
}
public class Sub1<SomeType> {
//now field is of type SomeType
}
public class Sub2<SomeOtherType> {
//now field is of type SomeOtherType
}
公共抽象类超类{
保护型字段;
}
公共课Sub1{
//现在字段的类型是SomeType
}
公开课Sub2{
//现在字段的类型为SomeOtherType
}
正如@GuillaumeDarmont所指出的,在构造中使用可重写方法是不好的做法
您希望强制超类id
由子类初始化,因此更改构造函数:
public abstract class SuperClass {
protected String id;
public SuperClass(String id) {
this.id = id;
}
}
此外,您可能希望将generateId()
更改为静态方法,因为在调用超类构造函数之前,您不能引用此
:
public class Sub1 extends SuperClass {
private SomeType fieldSub1;
public Sub1(SomeType fieldSub1) {
super(generateId(fieldSub1));
this.fieldSub1 = fieldSub1;
}
private static String generateId(SomeType fieldSub1) {
// Some operations that use fieldSub1
}
}
编辑:因为超类
不知道如何计算id
,但如果要强制执行它,则需要使用id,您的选项之一是上面的解决方案。另一种选择是:
public abstract class SuperClass {
private String id;
public String getId() {
if (id == null) { id = generateId(); }
return id;
}
protected abstract String generateId();
}
public class Sub1 extends SuperClass {
private SomeType fieldSub1;
public Sub1(SomeType fieldSub1) {
this.fieldSub1 = fieldSub1;
}
@Override protected String generateId() {
// Some operations that use fieldSub1
}
}
当计算id时,两种解决方案之间的差异约为:在对象初始化时,或在第一次请求id时。这就是@Turing85所讨论的。如果你让SomeType
和SomeOtherType
有一个共同的父类,然后将ParentType
传递给超类
构造函数会怎么样?@Arc676我无法想象它。您可以发布一个包含代码的答案吗?您可能需要检查以下问题:。简单地说:不要在构造函数中使用可重写的方法。你能解释一下你的具体问题是什么吗?我认为这可能是一个错误。为什么必须在初始化时生成id?为什么不能在首次使用id时及时计算id?我强烈反对这种方法,因为它牺牲了代码的可读性。@Turing85这只是作者的又一个选择。也许这是两害相权取其轻。谢谢你的贡献,但顺便说一句,在oop语义中,这个解决方案是不正确的,对吗?也就是说:超类
实际上不应该是泛型类。我不是特别确定。正如我所说,我以前从未尝试过这个,所以都是理论上的。我觉得这是不可能的,我的谷歌搜索给了我这个解决方案的链接,所以我认为它可能是适用的。如果我有时间的话,我可能会抽空来测试这个,但总是有可能超类
有一些特性禁止它成为抽象的
。你确定java接受你发布的最后一个构造吗?@ericbn这是一个解决方案,但这在语义上有多正确?也就是说:超类
的构造函数的参数实际上不是一个输入,而是可以用手头的可用数据计算出来的。那么,这样写不是让它看起来像是需要作为输入获取的东西吗?