Java – 如何处理构造函数中的类型擦除?

假设我class上有两个构造函数:

public User (List source){ ... } public User (List source) { ... } 

假设这两个构造函数都提供了有关用户的相同信息,并且是为不同用例构建用户的同等有效方法。

在Java中,由于类型擦除而无法执行此操作 – Java不会接受两个具有作为参数List 。

那么,解决这个问题的方法是什么? 什么是不是矫枉过正但仍然尊重基本OO的解决方案? 由于Java没有强大的generics支持,因此必须构建一个工厂方法或其他接口似乎是错误的。

以下是我能想到的可能性:

1)接受List作为构造函数的参数,并在构造函数中解析您需要哪种逻辑,或者如果它不是任何可接受的类型则抛出exception。

2)创建一个接受List的类,构造相应的User对象,然后返回它。

3)创建可以传递给User构造函数的ListList包装器。

4)将这个人子类化为两个类,除了构造函数之外,所有的function都是inheritance的。 一个的构造函数接受Source1,另一个接受Source2。

5)用一个构建器包装这个人,其中两个不同的构建器方法用于实例化两个不同的数据源。

我的问题是这些:

1)是否需要使用Java或有意的设计决策来做这个缺陷? 什么是直觉?

2)在保持良好代码而不引入不必要的复杂性方面哪种解决方案最强? 为什么?

这个问题是类似的: 在Java中围绕类型擦除设计构造函数但没有详细说明,它只是提出了各种解决方法。

通常的方法是使用工厂方法 :

 public static User createFromSource1(List source) { User user = new User(); // build your User object knowing you have Source1 data return user; } public static User createFromSource2(List source) { User user = new User(); // build your User object knowing you have Source2 data return user; } 

如果您只想使用Source1Source2构造(即您没有默认构造函数),则只需隐藏构造函数,强制客户端使用您的工厂方法:

 private User () { // Hide the constructor } 

出现这个问题的原因是你不能以不同的方式命名构造函数,如果这些是正常的方法,你将如何克服这个问题。 因为构造函数名称被固定为类名,所以这种代码模式只能区分,然后给出相同类型的擦除。

1:保持与擦除的向后兼容性。

2:你的class级可以使用generics吗? 像这样的东西:

 public class User { private List whatever; public User(List source){ .... } } 

我不确定这是不是你的意思(2)

基本问题是在存在generics之前设计了语言(构造函数名称是固定的),因此它不能处理由于类型擦除引起的冲突,类型擦除会通过重命名方法来区分它们来处理。

一个“解决方法”,不依赖于工厂方法,是添加另一个非类型参数,以使编译器能够区分它们:

 public User(List, Source1 instance) { // ignore instance } public User(List, Source2 instance) { // ignore instance } 

这有点蹩脚,因为你可以用任何东西替换那些额外的参数(例如IntegerString ,或者只是让其中一个省略第二个参数),它仍然可以工作。 此外,忽略额外参数 – 它仅用于区分构造函数。 尽管如此,它确实允许代码在不添加任何额外方法或特殊代码的情况下工作。

 public class User { public User(List source){ } } 

或者可能更好:

 public class User { public User(List source){ } } 

Someer SomeTypeOfYoursSource1Source2的超类型。

我一般喜欢Factory的想法,或者对User进行泛化(由@Markus Mikkolainen建议),但是一种可能的替代方法是将列表的类作为第二个参数传递并打开它,例如

 public User source, Class clazz) { switch(clazz) { case Source1.class: initForSource1(); break; case Source2.class: initForSource2(); break; default: throw new IllegalArgumentException(); } } 

如果有一些共同的祖先类, 可能是其他东西。 我可以想象很多情况下这是一个糟糕的想法,但有些可能是可以接受的。