【问题标题】:Constructor overloading in Java - best practiceJava中的构造函数重载 - 最佳实践
【发布时间】:2010-11-14 00:09:48
【问题描述】:

有几个类似的主题,但我找不到一个足够的答案。

我想知道 Java 中构造函数重载的最佳实践是什么。我已经对这个主题有了自己的想法,但我想听听更多的建议。

我指的是简单类中的构造函数重载和继承已经重载的类时的构造函数重载(意味着基类具有重载的构造函数)。

谢谢:)

【问题讨论】:

    标签: java constructor overloading constructor-overloading


    【解决方案1】:

    虽然没有“官方指南”,但我遵循 KISS 和 DRY 的原则。使重载的构造函数尽可能简单,最简单的方法是它们只调用 this(...)。这样你只需要检查和处理一次参数。

    public class Simple {
    
        public Simple() {
            this(null);
        }
    
        public Simple(Resource r) {
            this(r, null);
        }
    
        public Simple(Resource r1, Resource r2) {
            // Guard statements, initialize resources or throw exceptions if
            // the resources are wrong
            if (r1 == null) {
                r1 = new Resource();
            }
            if (r2 == null) {
                r2 = new Resource();
            }
    
            // do whatever with resources
        }
    
    }
    

    从单元测试的角度来看,测试类会变得很容易,因为您可以将资源放入其中。如果该类有很多资源(或一些 OO 极客称之为协作者),请考虑以下两件事之一:

    制作参数类

    public class SimpleParams {
        Resource r1;
        Resource r2;
        // Imagine there are setters and getters here but I'm too lazy 
        // to write it out. you can make it the parameter class 
        // "immutable" if you don't have setters and only set the 
        // resources through the SimpleParams constructor
    }
    

    Simple中的构造函数只需要拆分SimpleParams参数即可:

    public Simple(SimpleParams params) {
        this(params.getR1(), params.getR2());
    }
    

    …或将SimpleParams 设为属性:

    public Simple(Resource r1, Resource r2) {
        this(new SimpleParams(r1, r2));
    }
    
    public Simple(SimpleParams params) {
        this.params = params;
    }
    

    制作工厂类

    为你创建一个初始化资源的工厂类,如果初始化资源有点困难,这是有利的:

    public interface ResourceFactory {
        public Resource createR1();
        public Resource createR2();
    }
    

    然后以与参数类相同的方式完成构造函数:

    public Simple(ResourceFactory factory) {
        this(factory.createR1(), factory.createR2());
    } 
    

    将两者结合起来

    是的...您可以根据当时对您来说更容易的方式混合和匹配两种方式。考虑到Simple 类的使用方式相同,参数类和简单工厂类几乎是一回事。

    【讨论】:

    • 我注意到在这个以及其他示例中,构造函数定义的编写顺序在构造函数调用中使用最少到最多的参数。这是标准的 Java 风格吗?为什么?:在我看来,相反的方式是有意义的,因为你应该看到的第一个构造函数定义是包含所有细节的构造函数。
    • @JosieThompson 据我所知,这在任何标准中都没有定义。我同意首先拥有完整的构造函数对于快速查看方法的所有参数很有用;但是,按参数计数对它们进行排序,可以让您在页面下方跟踪重载调用,当您考虑我们如何阅读和编写代码时会感觉更自然。
    • 是否可以有多个构造函数,每个构造函数有 1 个不同类型的参数?喜欢 - new Object(int aa) / new Objec(String bb) ?
    【解决方案2】:

    我认为最佳实践是通过使用相关参数默认值调用this() 来让重载构造函数引用的单个主构造函数。这样做的原因是它使对象的构造状态更加清晰 - 实际上你可以将主构造器视为唯一真正的构造器,其他人只是委托给它

    这方面的一个例子可能是JTable - 主构造函数采用TableModel(加上列和选择模型),而其他构造函数调用这个主构造函数。

    对于子类父类已经有重载的构造函数,我倾向于认为将任何父类的构造函数视为主要的是合理的,并认为它是没有一个主构造函数是完全合法的。例如,在扩展 Exception 时,我经常提供 3 个构造函数,一个只接受 String 消息,一个接受 Throwable 原因,另一个接受两者。这些构造函数中的每一个都直接调用super

    【讨论】:

    • 我认为“已经重载的类”意味着基类有几个重载的构造函数。
    • 我已经从这个澄清中修改了我的答案
    • 我同意第一部分,但不同意继承已经重载的类的第二部分:假设我将 Exception 继承到一个新类,我希望 Exception 的字符串以 ' 开头bla' - 意思是我应该在接收字符串的构造函数中验证它。如果我不调用基类中的主构造函数,我必须复制此验证的代码。
    • 我认为这是不必要的限制。
    • @Tom - 有几次我会偏离这种做法,但我在这样做时总是会仔细考虑,因为我相信没有单个 primary 构造函数是一件好事类设计混乱的指标。
    【解决方案3】:

    如果您有一个非常复杂的类,其中有很多选项,其中只有一些组合是有效的,请考虑使用 Builder。在代码方面和逻辑上都工作得很好。

    Builder 是一个嵌套类,其方法仅用于设置字段,然后 ComplexClass 构造函数仅将这样的 Builder 作为参数。


    编辑:ComplexClass 构造函数可以确保 Builder 中的状态有效。如果您只在 ComplexClass 上使用 setter,这将很难做到。

    【讨论】:

    • 非常同意使用构建器模式,像 OP 这样的重载构造器确实是个坏主意,因为它不符合最小意外原则。
    【解决方案4】:

    这实际上取决于类的类型,因为并非所有类都是平等的。

    作为一般准则,我建议 2 个选项:

    • 对于 值和不可变 类(异常、整数、DTO 等),请按照上述答案中的建议使用单一主构造函数
    • 对于其他所有内容(会话 bean、服务、可变对象、JPA 和 JAXB 实体等),仅使用默认构造函数,所有属性都具有合理的默认值,因此无需额外配置即可使用它

    【讨论】:

      【解决方案5】:

      构造函数重载类似于方法重载。构造函数可以被重载,以不同的方式创建对象。

      编译器根据构造函数中存在的参数数量和其他参数(例如参数传递的顺序)来区分构造函数。

      更多java构造函数详情请访问https://tecloger.com/constructor-in-java/

      【讨论】:

        【解决方案6】:

        嗯,这是一个重载构造函数的例子。

        public class Employee
        {
           private String name;
           private int age;
           
           public Employee()
           {
              System.out.println("We are inside Employee() constructor");
           }
        
           public Employee(String name)
           {
              System.out.println("We are inside Employee(String name) constructor");
              this.name = name;
           }
        
           public Employee(String name, int age)
           {
              System.out.println("We are inside Employee(String name, int age) constructor");
              this.name = name;
              this.age = age;
           }
        
           public Employee(int age)
           {
              System.out.println("We are inside Employee(int age) constructor");
              this.age = age; 
           }
        
           public String getName()
           {
              return name;
           }
           
           public void setName(String name)
           {
              this.name = name;
           }
        
           public int getAge()
           {
              return age;
           }
           
           public void setAge(int age)
           {
              this.age = age;
           }
        }
        

        在上面的示例中,您可以看到重载的构造函数。构造函数名称相同,但每个构造函数的参数不同。

        这里有一些资源可以更清楚地说明 java 中的构造函数重载,

        Constructors.

        【讨论】:

        • 似乎在写这个答案时付出的努力很少。 OP 知道什么是构造函数重载。答案就说明了这一点。
        猜你喜欢
        • 2018-10-13
        • 1970-01-01
        • 1970-01-01
        • 2017-09-13
        • 1970-01-01
        • 2016-09-12
        • 2011-04-17
        • 2022-11-04
        • 1970-01-01
        相关资源
        最近更新 更多