Java 带有泛型的静态工厂方法
我在Java中遇到了一些泛型问题。我有一个通用的基类Java 带有泛型的静态工厂方法,java,generics,Java,Generics,我在Java中遇到了一些泛型问题。我有一个通用的基类a,它定义了一个静态工厂方法,用初始值创建a的实例。我这样做是为了在创建A的实例时设置一些初始值,而不是指定一个设置初始值的构造函数,因为这将涉及调用构造函数中的可重写方法(这是非常不鼓励的) 我希望能够使用B中的create静态方法来创建带有初始项的B实例。尝试调用B.create(…)时,我收到一个错误,内容如下: 错误:(5,23)java:不兼容的类型:不存在类型变量T,E的实例,因此A符合B 因此,我决定尝试为B类定义create的实
a
,它定义了一个静态工厂方法,用初始值创建a
的实例。我这样做是为了在创建A
的实例时设置一些初始值,而不是指定一个设置初始值的构造函数,因为这将涉及调用构造函数中的可重写方法(这是非常不鼓励的)
我希望能够使用B
中的create
静态方法来创建带有初始项的B
实例。尝试调用B.create(…)
时,我收到一个错误,内容如下:
错误:(5,23)java:不兼容的类型:不存在类型变量T,E的实例,因此A符合B
因此,我决定尝试为B
类定义create
的实现:
public static B create(List<String> values) {
B b = new B();
b.setValues(values);
return b;
}
publicstaticb创建(列表值){
B=新的B();
b、 设置值(值);
返回b;
}
但这次我得到了一个不同的错误:
错误:(8,21)B中的java:name clash:create(java.util.List)和A中的create(java.util.List)具有相同的擦除,但两者都不隐藏另一个
虽然我理解每个错误的有效含义,但我不知道如何避开它们。我如何在
a
上定义一个与任何子类一起工作的静态工厂方法,或者如何在a
上定义一个静态工厂方法,以及在覆盖/隐藏a
实现的a
的每个适用子类上定义一个静态工厂方法。也就是说,您将静态工厂方法放在非final类中
我想提出一种不同的方法:创建一个“
使用复数名称(例如,SomeType
->SomeTypes
)的“companion”实用程序类,并将静态工厂放在那里。然后对你的其他类型做同样的事情
由于这种方法,您无法(甚至意外地)获得与预期不同的类型(现在,调用
B.create
生成a
的实例)。这里可能没有完美的答案。有几个选项可能是有意义的,但细节可能取决于应用案例。我将列出一些关于以下问题的选项:静态工厂方法应该在哪里,它们的名称应该是什么?
A
中的方法创建了A
的一个实例,您无法通过调用它“through”B
将其神奇地更改为B
B.create()
仍将创建一个A
,无论您做什么
正如您所观察到的,在B
中创建create()
方法将导致名称冲突
ClassName
的类,有一个名为ClassNames
的类包含创建实例的方法
当类名没有合理的复数形式时,我感到很烦,尽管
例如:
class Customer { /* With package-private constructor, usually ... */ }
public final class Customers {
public static Customer create() { ... }
}
class PremiumCustomer { /* With package-private constructor, usually ... */ }
public final class PremiumCustomers {
public static PremiumCustomer create() { ... }
}
这样做的好处是,您可以轻松添加扩展Customer
的类,而不会影响现有的代码库
这里的一个小缺点是,您不能使用方法的静态导入,因为它们都有相同的名称,但在实际代码中这很少是一个问题
A
和B
,这并不表示实际的结构。但根据这种结构,一个类使用不同的工厂方法(使用不同的名称)来表示专门化可能更有意义:
public final class Customers {
public static Customer createStandard() { ... }
public static PremiumCustomer createPremium() { ... }
}
后一种选择哪一种“更好”很难说。这还取决于包结构:当类位于不同的包中时,不能将其构造函数包私有,这可能是不可取的
一个可能很重要的注意事项:您专门询问了工厂方法。但是您描述的场景提出了另一个问题:谁应该调用这些方法,以及如何调用? 每个人都必须知道他想要创建的实例的确切类型 一般来说,当涉及多态性和继承时,
静态
方法总是会出现问题,可能会引起麻烦。您应该考虑使用(“简单”的、非静态的)工厂。p>
当你有这个:
void example() {
Customer customer = Customer.create();
doSomeComplexStuffWith(customer);
}
然后,实现者必须显式调用Customer.create()
(或Customers.create()
或Customers.createDefault()
——这在这里并不重要)
无法更改在此处创建的图元的类型
在此,您可以考虑将静态工厂方法的使用更改为<代码>供应商 >:
void example(Supplier<? extends Customer> supplier) {
Customer customer = supplier.get();
doSomeComplexStuffWith(customer);
}
但同样,这取决于您首先打算如何创建和使用实例
一句小评论: 正在构造函数中调用可重写的方法。。。他非常气馁 这是真的。但是当你有一个静态的工厂方法时,你可以把它看作是一个拐角的例子,并使类的构造函数<代码>私下<代码>或<代码>保护< /代码>。然后,您可以建立一个契约,对创建过程有更多的控制,并确保重写的方法不会产生不良影响 但一般来说,工厂可能确实不需要在构造函数中调用可重写的方法 我如何定义一个与 任何子类
void example() {
Customer customer = Customer.create();
doSomeComplexStuffWith(customer);
}
void example(Supplier<? extends Customer> supplier) {
Customer customer = supplier.get();
doSomeComplexStuffWith(customer);
}
example(Customers::createStandard); // Run example with standard customers
example(Customers::createPremium); // Run example with premium customers
public class A<T> {
private List<T> values;
public A() {
this.values = new ArrayList<>();
}
public static <T> A<T> createA(List<T> values) {
A<T> a = new A<>();
a.setValues(values);
return a;
}
}
public class B extends A<Integer> {
public B() {
super();
}
public static B createB(List<Integer> values) {
B b = new B();
b.setValues(values);
return b;
}
}
A<String> a = A.createA(Arrays.asList("hello" ,"you"));
B b = B.createB(Arrays.asList(1 ,2));
public final class MyFactory{
public static <T, U extends A<T>> U create(Supplier<U> supplierA, List<T> values) {
U a = supplierA.get();
a.setValues(values);
return a;
}
}
public class A<T> {
public void setValues(List<T> values) {
this.values.clear();
for (T value : values) {
this.values.add(this.modify(value));
}
}
}
public class B extends A<Integer> {
}
A<String> list = MyFactory.create(A::new, Arrays.asList("hello", "you"));
B b = MyFactory.create(B::new, Arrays.asList(1, 2));