Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/361.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何在不重复代码的情况下编写子类构造函数?_Java_Oop_Inheritance - Fatal编程技术网

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这是一个解决方案,但这在语义上有多正确?也就是说:
    超类
    的构造函数的参数实际上不是一个输入,而是可以用手头的可用数据计算出来的。那么,这样写不是让它看起来像是需要作为输入获取的东西吗?