gpt4 book ai didi

java - 带有泛型的静态工厂方法

转载 作者:行者123 更新时间:2023-11-30 07:45:35 25 4
gpt4 key购买 nike

我在使用 Java 中的泛型时遇到了一些麻烦。我有一个通用基类 A它定义了一个静态工厂方法来创建 A 的实例带有初始值。我这样做是为了在创建 A 的实例时设置一些初始值而不是指定一个设置初始值的构造函数,因为它会涉及在构造函数中调用可覆盖的方法(强烈建议不要这样做)。

public class A<T> {
private List<T> values;

public A() {
this.values = new ArrayList<>();
}

public void setValues(List<T> values) {
this.values.clear();
for (T value : values) {
this.values.add(this.modify(value));
}
}

protected T modify(T value) {
// Make modifications to value before it is stored. My real implementation actually does stuff.
return value;
}

public static <T> A<T> create(List<T> values) {
A<T> a = new A<>();
a.setValues(values);
return a;
}
}

我还有一门课 B扩展 A并指定要使用的泛型类型。
public class B extends A<Integer> {
public B() {
super();
}
}

我希望能够使用 create B 中的静态方法创建 B 的实例与初始项目。尝试调用 B.create(...) 时我收到一条错误消息:

Error:(5, 23) java: incompatible types: no instance(s) of type variable(s) T,E exist so that A conforms to B



所以我决定尝试定义 create 的实现。特定于 B类(class):
public static B create(List<String> values) {
B b = new B();
b.setValues(values);
return b;
}

但是这次我得到了一个不同的错误:

Error:(8, 21) java: name clash: create(java.util.List) in B and create(java.util.List) in A have the same erasure, yet neither hides the other



虽然我理解每个错误的有效含义,但我不知道如何绕过它们。如何在 A 上定义静态工厂方法?可以与任何子类一起使用,或者如何在 A 上定义静态工厂方法以及 A 的每个适用子类上的静态工厂方法覆盖/隐藏 A的实现?

最佳答案

这里可能没有完美的答案。有几个选项可能有意义,但细节可能取决于应用案例。我将列出一些关于 问题的选项。静态工厂方法应该在哪里,它们的名称应该是什么?

  • 将它们放入类

  • 这是您现在所做的,但可能有一些警告: A 中的方法创建 A 的实例,并且您不能神奇地将其更改为 B只需称其为“通过” B . B.create()仍将创建 A , 无论你做什么。

    并创建一个 create() B 中的方法如您所见,将导致名称冲突。

  • 将它们放入与它们所操作的类相对应的类中

  • Tomasz Linkowski的方法|他的回答中提到的做法并不少见:对于称为 ClassName 的类(class),有一个类叫 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 的类。 ,而不影响现有的代码库。

    这里的一个小缺点是您不能使用方法的静态导入,因为它们都具有相同的名称,但这在实际代码中很少会成为问题。

  • 将它们放入一个聚合不同类型的类中

  • 你的类(class)被称为 AB ,它没有传达实际的结构。但是根据这种结构,让一个类具有不同的工厂方法(具有不同的名称)来指示特化可能更有意义:
    public final class Customers {
    public static Customer createStandard() { ... }
    public static PremiumCustomer createPremium() { ... }
    }

    很难说后一种选择哪个“更好”。它还取决于包结构:当类位于不同的包中时,您不能将它们的构造函数设为包私有(private),这可能是不可取的。

    可能很重要的注释:您专门询问了 static 工厂方法。但是您描述的情况引发了另一个问题: 谁应该调用这些方法,如何调用?

    每个人都必须知道他想要创建的实例的确切类型......

    一般来说,当涉及到多态和继承时, static方法总是失败,并可能导致头痛。您应该考虑改用(“简单”,非静态)工厂。

    当你有这个:
    void example() {
    Customer customer = Customer.create();
    doSomeComplexStuffWith(customer);
    }

    那么实现者必须显式调用 Customer.create() (或 Customers.create()Customers.createDefault() - 这无关紧要)。

    无法更改在那里创建的元素的类型。

    此时,您可以考虑将静态工厂方法的使用更改为 Supplier 的使用。 :
    void example(Supplier<? extends Customer> supplier) {
    Customer customer = supplier.get();
    doSomeComplexStuffWith(customer);
    }

    这样,您可以将静态方法用作实际的工厂实例,可能用作方法引用。 example的执行方法不受此处类型更改的影响:
    example(Customers::createStandard); // Run example with standard customers
    example(Customers::createPremium); // Run example with premium customers

    但同样,这取决于您首先打算如何创建和使用实例。

    一个小评论:

    calling overridable methods in the constructor ... is strongly discouraged



    这是真实的。但是当你有一个静态工厂方法时,可以将其视为一种极端情况,并制作类的构造函数 privateprotected .然后,您可以建立一个合约,对创建过程有更多的控制权,并确保没有被覆盖的方法引起的不良影响。

    但总的来说,工厂可能确实不需要在构造函数中调用可覆盖的方法。

    关于java - 带有泛型的静态工厂方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51684445/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com