构造函数重载 - Java 中的最佳实践 [关闭]
Posted
技术标签:
【中文标题】构造函数重载 - Java 中的最佳实践 [关闭]【英文标题】:Constructor overloading - best practice in Java [closed] 【发布时间】:2018-10-13 09:38:22 【问题描述】:构造函数也可以像任何其他方法一样被重载,我知道这一事实。由于一项任务,我决定使用具有多个构造函数的抽象超类:
抽象超类:
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 );
在这种情况下,子类的每个构造函数都会立即调用超类的构造函数。我想到我可以再次引用自己的构造函数并传递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 个重载构造函数,但会强制每个子类遵循相同的模式。
我的问题是:在一致性的情况下,更好的方法是什么?处理抽象超类或子类中的缺失值?它对实例化有影响还是只是意见问题?
【问题讨论】:
当然,这是主观意见。从干净的代码和没有不必要的重复的角度来看,超类中只有一个构造函数。无论如何,子类中都需要有多个构造函数,而超类不能由客户端代码直接构造。 我的建议是使用构建器模式为您构建ListBubbleSort
实例。
@xxxvodnikxxx 你可能是对的,这个问题也适合Code Review,但我显然想知道关于实例化是否有任何技术影响。关于公约。这不仅是我公司的风格,也是所谓的Allman convention,在java中并不少见。
谨防使用“最佳实践”这个短语:satisfice.com/blog/archives/27、innovationexcellence.com/blog/2013/03/09/…、forbes.com/sites/mikemyatt/2012/08/15/best-practices-arent
@StephenC 强烈不同意。无论如何,您发布的两篇文章似乎在很大程度上攻击了一个稻草人。
【参考方案1】:
在保持一致性的情况下,有什么更好的方法?
-
将所有子构造函数设为私有。
引入静态工厂方法。
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;
Don't use Optional
as a field.
this.original = Optional.ofNullable(li);
如果您有 3 个以上的参数,请考虑 the Builder Pattern。
处理抽象超类或子类中的缺失值?
构造函数应该提供初始值。 您没有传递初始值,您只是表明它们不存在。
默认情况下,null
是引用类型的初始值。因此,如果没有给出值,则无需重新分配字段。
它对实例化有影响还是只是意见问题?
可读性、维护性。
我会推荐阅读Effective Java by Joshua Bloch:
创建和销毁对象
第 1 项:考虑静态工厂方法而不是构造函数 第 2 项:在面对许多构造函数参数时考虑使用构建器【讨论】:
【参考方案2】:关于问题本身:我认为这两种选择都不理想。
您努力编写尽可能少的代码。添加重载只是因为它们看起来方便,所以你要小心。实际上,您应该反其道而行之:认真思考您的真实用例是什么,并且只支持那些。
在您的情况下,练习的重点似乎是允许使用不同的实现进行排序。从这个意义上说,您应该例如查看strategy pattern。
换句话说:你的第一个想法总是更喜欢组合而不是继承。而当您的设计将您引向此类问题时,更好 的答案可能是从您当前的设计中退后一步,并找到一种方法来启用不同的排序作为某种“服务”——而不是支持 strong>list 将自身组合成一个排序的东西。
【讨论】:
【参考方案3】:这样做可以让我省略 3 个重载的构造函数 抽象超类,但会强制每个子类都遵循 相同的模式。
执行合同是通过抽象方法或接口实现的。你不能确定每个子类都会有这些构造函数,或者至少你不能确定每个子类都会正确地添加这些构造函数。
所以,考虑到Encapsulation,这些构造函数最好放在超类中。
【讨论】:
【参考方案4】:这样做可以让我省略 3 个重载的构造函数 抽象超类,但会强制每个子类都遵循 相同的模式。
子类没有任何约束来调用父类的特定构造函数。它可以调用其中任何一个,而这会调用其中一个。 所以你无法达到这样的要求。
关于使用静态工厂方法的解决方案,在某些情况下它是可以接受的,甚至可以接受,但它不是奇迹,也有一些限制。 例如:它不允许切换到另一个实现。 对于 JDK 类,我同意这几乎不是问题,但对于自定义类,我们应该谨慎使用它。 此外,没有什么可以阻止子类扩展父类并且不通过工厂来创建该子类的实例。 因此,也不能保证引用的要求。
我的问题是:在以下情况下,更好的方法是什么? 一致性?处理抽象超类或 子类?它对实例化有影响吗? 只是意见问题?
这不是意见问题。这很重要。 如果子类委托给父类并且检查总是相同的,让父类执行检查似乎很有趣。 但是,如果您不能保证父类不会对检查进行修改,那么如果父类更改其检查,子类可能会创建一些不一致的实例。
【讨论】:
你答案的最后一个部分有一个我还没有真正考虑过的非常强的观点。感谢您的洞察力!【参考方案5】:你可以添加一个最终的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 行为相同。然而,builder 模式 可以防止不必要的样板代码。在最初的旧 Java 中,许多构造函数是要走的路,但现在人们在 API 中看到了许多构建器。
对于列表排序器,使用某种构建器的流畅 API 可能是一个想法。
所以最终的答案是:更多的是上下文风格决定。
【讨论】:
以上是关于构造函数重载 - Java 中的最佳实践 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章