构造函数重载 - Java 中的最佳实践
Constructor overloading - best practice in Java
构造函数也可以像任何其他方法一样被重载,我知道这一点。由于一项任务,我决定使用具有多个构造函数的抽象超类:
抽象超类:
protected ListSortierer()
{
this( null, null );
}
protected ListSortierer( List<E> li )
{
this( li, null );
}
protected ListSortierer( Comparator<E> comp )
{
this( null, comp );
}
protected ListSortierer( List<E> li, Comparator<E> com )
{
this.original = Optional.ofNullable( li );
this.comp = Optional.ofNullable( com );
}
为了访问这些构造函数中的每一个,我在子类中也需要多个构造函数。
BubbleSort.java:
public ListBubbleSort()
{
super();
}
public ListBubbleSort( List<E> li )
{
super( li );
}
public ListBubbleSort( Comparator<E> com )
{
super( com );
}
public ListBubbleSort( List<E> li, Comparator<E> com )
{
super( li, com );
}
在这种情况下,子类的每个构造函数都调用超类的构造函数 immediately.It 我想到我可以再次引用自己的构造函数并传递 null
值:
public ListBubbleSort()
{
this( null, null );
}
public ListBubbleSort( List<E> li )
{
this( li, null );
}
public ListBubbleSort( Comparator<E> com )
{
this( null, com );
}
public ListBubbleSort( List<E> li, Comparator<E> com )
{
super( li, com );
}
这样做可以让我省略抽象超类中的 3 个重载构造函数,但会强制每个子类都遵循相同的模式。
我的问题是:在一致性的情况下,更好的方法是什么?处理抽象超类或子类中的缺失值?它对实例化有影响还是只是一个意见问题?
Doing so would allow me to omit 3 of the overloaded constructors in
the abstract superclass but would enforce that every subclass follows
the same pattern.
执行合同是通过抽象方法或接口实现的。你不能确定每个子类都会有这些构造函数,或者至少你不能确定每个子类都会正确地添加这些构造函数。
因此,考虑到 Encapsulation,这些构造函数最好放在超类中。
关于问题本身:我认为这两种选择都不理想。
您努力编写尽可能少的代码。您在添加重载时非常小心,因为它们看起来 方便 。实际上,您应该反其道而行之:认真思考您的 真实 用例是什么,并且只支持这些用例。
在您的情况下,整个练习的重点似乎是允许使用不同的实现进行排序。从这个意义上说,您应该查看 strategy pattern 示例。
换句话说:您的第一个想法始终是更喜欢组合而不是继承。当您的设计将您引向此类问题时,更好的 答案可能是从您当前的设计退后一步,并找到一种方法来启用不同的排序,如某种 "service" -而不是将 list 本身一起支持排序。
What is a better approach in case of consistency?
- 将所有子构造函数设为私有。
引入静态工厂方法。
ListBubbleSort.withList(List<E> list)
ListBubbleSort.withComparator(Comparator<E> comparator)
调用适当的 super
构造函数。不要传递任何 null
s.
public static <E> ListBubbleSort withList(List<E> list) {
return new ListBubbleSort(list);
}
private ListBubbleSort(List<E>) {
super(list);
}
protected ListSortierer(List<E>) {
// initialise only the list field
this.origin = list;
}
this.original = Optional.ofNullable(li);
如果参数超过 3 个,请考虑 the Builder Pattern。
Handle missing values in the abstract superclass or in the subclass?
构造函数应该提供初始值。
您没有传递初始值,您只是 表明它们不存在 。
默认情况下,null
是引用类型的初始值。因此,如果尚未给出字段值,则无需重新分配字段。
Does it make a difference regarding instantiation or is it just a matter of opinion?
可读性,维护。
我会推荐阅读 Effective Java by Joshua Bloch:
创建和销毁对象
- 项目 1:考虑静态工厂方法而不是构造函数
- 项目 2:面对许多构造函数参数时考虑构建器
您可以添加一个 final addAll
,作为可选的附加项在子构造函数中调用。对于比较器,语义不同,它(几乎)必须在可选时重载。
private final Optional<Comparator<E>> comparatorOption;
public final void addAll(List<E> li) {
...;
}
protected ListSortierer() {
comparatorOption = Optional.empty();
}
protected ListSortierer(Comparator<E> comp) {
comparatorOption = Optional.of(comp);
}
避免空参数似乎更好。
如果您愿意,通过拥有许多构造函数来规定所有子类 具有相同的构造函数也不错。优点是所有 类 的 API 表现相同。然而,这是不必要的样板代码,构建器模式 可以防止。在最初的旧 java 中,许多构造函数是要走的路,但现在人们在 APIs 中看到许多构造函数。
对于列表排序器,使用某种生成器可以流畅地API。
所以最后的答案:更多的是上下文风格决定。
Doing so would allow me to omit 3 of the overloaded constructors in
the abstract superclass but would enforce that every subclass follows
the same pattern.
子class 没有任何约束来调用父class 的特定构造函数。它可以调用其中任何一个,而这会调用其中一个。
所以你无法达到这样的要求。
关于使用静态工厂方法的解决方案,在某些情况下是可以接受的,甚至还可以,但它不是奇迹,也有一些局限性。
例如:它不允许切换到另一个实现。
对于 JDK classes,我同意这几乎从来都不是问题,但对于自定义 class 我们应该谨慎使用它。
此外,没有什么可以阻止 subclass 扩展父 class 并且不通过工厂来创建此 subclass.
的实例
因此,也不能确保引用的要求。
My question is: What is the the better approach in case of
consistency?Handle missing values in the abstract superclass or in the
subclass? Does it make a difference regarding instantiation or is it
just a matter of opinion?
这不是意见问题。这很重要。
如果子 class 委托给父 class 并且检查总是相同的,那么让父 class 执行检查似乎很有趣。
但是,如果您不能保证父 class 永远不会修改检查,则子 class 可能会在父 class 更改其检查时创建一些不一致的实例。
构造函数也可以像任何其他方法一样被重载,我知道这一点。由于一项任务,我决定使用具有多个构造函数的抽象超类:
抽象超类:
protected ListSortierer()
{
this( null, null );
}
protected ListSortierer( List<E> li )
{
this( li, null );
}
protected ListSortierer( Comparator<E> comp )
{
this( null, comp );
}
protected ListSortierer( List<E> li, Comparator<E> com )
{
this.original = Optional.ofNullable( li );
this.comp = Optional.ofNullable( com );
}
为了访问这些构造函数中的每一个,我在子类中也需要多个构造函数。
BubbleSort.java:
public ListBubbleSort()
{
super();
}
public ListBubbleSort( List<E> li )
{
super( li );
}
public ListBubbleSort( Comparator<E> com )
{
super( com );
}
public ListBubbleSort( List<E> li, Comparator<E> com )
{
super( li, com );
}
在这种情况下,子类的每个构造函数都调用超类的构造函数 immediately.It 我想到我可以再次引用自己的构造函数并传递 null
值:
public ListBubbleSort()
{
this( null, null );
}
public ListBubbleSort( List<E> li )
{
this( li, null );
}
public ListBubbleSort( Comparator<E> com )
{
this( null, com );
}
public ListBubbleSort( List<E> li, Comparator<E> com )
{
super( li, com );
}
这样做可以让我省略抽象超类中的 3 个重载构造函数,但会强制每个子类都遵循相同的模式。
我的问题是:在一致性的情况下,更好的方法是什么?处理抽象超类或子类中的缺失值?它对实例化有影响还是只是一个意见问题?
Doing so would allow me to omit 3 of the overloaded constructors in the abstract superclass but would enforce that every subclass follows the same pattern.
执行合同是通过抽象方法或接口实现的。你不能确定每个子类都会有这些构造函数,或者至少你不能确定每个子类都会正确地添加这些构造函数。
因此,考虑到 Encapsulation,这些构造函数最好放在超类中。
关于问题本身:我认为这两种选择都不理想。
您努力编写尽可能少的代码。您在添加重载时非常小心,因为它们看起来 方便 。实际上,您应该反其道而行之:认真思考您的 真实 用例是什么,并且只支持这些用例。
在您的情况下,整个练习的重点似乎是允许使用不同的实现进行排序。从这个意义上说,您应该查看 strategy pattern 示例。
换句话说:您的第一个想法始终是更喜欢组合而不是继承。当您的设计将您引向此类问题时,更好的 答案可能是从您当前的设计退后一步,并找到一种方法来启用不同的排序,如某种 "service" -而不是将 list 本身一起支持排序。
What is a better approach in case of consistency?
- 将所有子构造函数设为私有。
引入静态工厂方法。
ListBubbleSort.withList(List<E> list) ListBubbleSort.withComparator(Comparator<E> comparator)
调用适当的
super
构造函数。不要传递任何null
s.public static <E> ListBubbleSort withList(List<E> list) { return new ListBubbleSort(list); } private ListBubbleSort(List<E>) { super(list); } protected ListSortierer(List<E>) { // initialise only the list field this.origin = list; }
this.original = Optional.ofNullable(li);
如果参数超过 3 个,请考虑 the Builder Pattern。
Handle missing values in the abstract superclass or in the subclass?
构造函数应该提供初始值。 您没有传递初始值,您只是 表明它们不存在 。
默认情况下,null
是引用类型的初始值。因此,如果尚未给出字段值,则无需重新分配字段。
Does it make a difference regarding instantiation or is it just a matter of opinion?
可读性,维护。
我会推荐阅读 Effective Java by Joshua Bloch:
创建和销毁对象
- 项目 1:考虑静态工厂方法而不是构造函数
- 项目 2:面对许多构造函数参数时考虑构建器
您可以添加一个 final addAll
,作为可选的附加项在子构造函数中调用。对于比较器,语义不同,它(几乎)必须在可选时重载。
private final Optional<Comparator<E>> comparatorOption;
public final void addAll(List<E> li) {
...;
}
protected ListSortierer() {
comparatorOption = Optional.empty();
}
protected ListSortierer(Comparator<E> comp) {
comparatorOption = Optional.of(comp);
}
避免空参数似乎更好。
如果您愿意,通过拥有许多构造函数来规定所有子类 具有相同的构造函数也不错。优点是所有 类 的 API 表现相同。然而,这是不必要的样板代码,构建器模式 可以防止。在最初的旧 java 中,许多构造函数是要走的路,但现在人们在 APIs 中看到许多构造函数。
对于列表排序器,使用某种生成器可以流畅地API。
所以最后的答案:更多的是上下文风格决定。
Doing so would allow me to omit 3 of the overloaded constructors in the abstract superclass but would enforce that every subclass follows the same pattern.
子class 没有任何约束来调用父class 的特定构造函数。它可以调用其中任何一个,而这会调用其中一个。
所以你无法达到这样的要求。
关于使用静态工厂方法的解决方案,在某些情况下是可以接受的,甚至还可以,但它不是奇迹,也有一些局限性。
例如:它不允许切换到另一个实现。
对于 JDK classes,我同意这几乎从来都不是问题,但对于自定义 class 我们应该谨慎使用它。
此外,没有什么可以阻止 subclass 扩展父 class 并且不通过工厂来创建此 subclass.
的实例
因此,也不能确保引用的要求。
My question is: What is the the better approach in case of consistency?Handle missing values in the abstract superclass or in the subclass? Does it make a difference regarding instantiation or is it just a matter of opinion?
这不是意见问题。这很重要。
如果子 class 委托给父 class 并且检查总是相同的,那么让父 class 执行检查似乎很有趣。
但是,如果您不能保证父 class 永远不会修改检查,则子 class 可能会在父 class 更改其检查时创建一些不一致的实例。