74 java constructor field
我一直想知道在Java中处理多个构造函数的最佳(即最干净/最安全/最有效)方法是什么?特别是在一个或多个构造函数中,并非所有字段都指定:
public class Book
{
private String title;
private String isbn;
public Book()
{
//nothing specified!
}
public Book(String title)
{
//only title!
}
...
}
Run Code Online (Sandbox Code Playgroud)
如果未指定字段,我该怎么办?到目前为止,我一直在使用类中的默认值,以便字段永远不为空,但这是一种"好"的做事方式吗?
Cra*_*lin 141
一个稍微简化的答案:
public class Book
{
private final String title;
public Book(String title)
{
this.title = title;
}
public Book()
{
this("Default Title");
}
...
}
Run Code Online (Sandbox Code Playgroud)
kgr*_*rad 36
考虑使用Builder模式.它允许您在参数上设置默认值,并以简洁明了的方式初始化.例如:
Book b = new Book.Builder("Catcher in the Rye").Isbn("12345")
.Weight("5 pounds").build();
Run Code Online (Sandbox Code Playgroud)
编辑:它还消除了对具有不同签名的多个构造函数的需求,并且更具可读性.
Luc*_*lle 19
您需要指定什么是类不变量,即对于类的实例始终为true的属性(例如,书的标题永远不会为null,或者狗的大小将始终> 0).
这些不变量应该在构造过程中建立,并在对象的生命周期内保留,这意味着方法不应该破坏不变量.构造函数可以通过使用强制参数或通过设置默认值来设置这些不变量:
class Book {
private String title; // not nullable
private String isbn; // nullable
// Here we provide a default value, but we could also skip the
// parameterless constructor entirely, to force users of the class to
// provide a title
public Book()
{
this("Untitled");
}
public Book(String title) throws IllegalArgumentException
{
if (title == null)
throw new IllegalArgumentException("Book title can't be null");
this.title = title;
// leave isbn without value
}
// Constructor with title and isbn
}
Run Code Online (Sandbox Code Playgroud)
但是,这些不变量的选择在很大程度上取决于你所写的课程,你将如何使用它等等,所以对你的问题没有明确的答案.
Law*_*Dol 12
你应该总是构造一个有效的合法对象; 如果您不能使用构造函数参数,则应使用构建器对象创建一个构建器对象,仅在对象完成时从构建器中释放对象.
关于构造函数使用的问题:我总是尝试使用一个所有其他人都遵循的基本构造函数,将"省略"参数链接到下一个逻辑构造函数并以基本构造函数结束.所以:
class SomeClass
{
SomeClass() {
this("DefaultA");
}
SomeClass(String a) {
this(a,"DefaultB");
}
SomeClass(String a, String b) {
myA=a;
myB=b;
}
...
}
Run Code Online (Sandbox Code Playgroud)
如果这是不可能的,那么我尝试使用所有构造函数遵循的私有init()方法.
并保持构造函数和参数的数量很少 - 每个最多5个作为指导.
可能值得考虑使用静态工厂方法而不是构造函数。
我说不是,但显然你不能代替的构造。但是,您可以做的是将构造函数隐藏在静态工厂方法后面。这样,我们将静态工厂方法作为类 API 的一部分发布,但同时我们隐藏了构造函数,使其成为私有或包私有的。
这是一个相当简单的解决方案,特别是与 Builder 模式相比(如 Joshua Bloch 的Effective Java 2nd Edition 中所见- 请注意,Gang of Four 的设计模式定义了一个完全不同的同名设计模式,因此可能会有点混乱)意味着创建嵌套类、构建器对象等。
这种方法在您和您的客户之间增加了一个额外的抽象层,加强了封装并使以后的更改更容易。它还为您提供实例控制——因为对象是在类内部实例化的,您而不是客户端决定何时以及如何创建这些对象。
最后,它使测试更容易——提供一个愚蠢的构造函数,它只是将值分配给字段,而不执行任何逻辑或验证,它允许您将无效状态引入您的系统以测试它的行为和反应。如果您在构造函数中验证数据,您将无法做到这一点。
您可以在(已经提到的)Joshua Bloch 的Effective Java 2nd Edition 中阅读更多相关内容——它是所有开发人员工具箱中的重要工具,难怪它是本书第一章的主题。;-)
按照你的例子:
public class Book {
private static final String DEFAULT_TITLE = "The Importance of Being Ernest";
private final String title;
private final String isbn;
private Book(String title, String isbn) {
this.title = title;
this.isbn = isbn;
}
public static Book createBook(String title, String isbn) {
return new Book(title, isbn);
}
public static Book createBookWithDefaultTitle(String isbn) {
return new Book(DEFAULT_TITLE, isbn);
}
...
Run Code Online (Sandbox Code Playgroud)
}
无论您选择哪种方式,最好有一个主构造函数,它只是盲目地分配所有值,即使它只是被另一个构造函数使用。
一些一般的构造函数提示:
了解构造函数的初始化规则的顺序.它基本上是:
整体流程最终是:
对于邪恶的一个很好的例子,尝试弄清楚下面将打印什么,然后运行它
package com.javadude.sample;
/** THIS IS REALLY EVIL CODE! BEWARE!!! */
class A {
private int x = 10;
public A() {
init();
}
protected void init() {
x = 20;
}
public int getX() {
return x;
}
}
class B extends A {
private int y = 42;
protected void init() {
y = getX();
}
public int getY() {
return y;
}
}
public class Test {
public static void main(String[] args) {
B b = new B();
System.out.println("x=" + b.getX());
System.out.println("y=" + b.getY());
}
}
Run Code Online (Sandbox Code Playgroud)
我将添加评论,说明为什么上面的工作原理......有些可能是显而易见的; 有些不是......
| 归档时间: |
|
| 查看次数: |
176183 次 |
| 最近记录: |