Java:通过返回Supplier vs返回新实例来实例化泛型类型

Jos*_*z_2 6 java generics java-8

我正在阅读如何实例化一个通用的,并在阅读并应用这个答案之后 ; 我想知道期待Supplier<T>与预期新实例之间的区别T.

例:

abstract class AbstractService<T extends AbstractEntity> {
    protected Supplier<T> makeNewThing();  // supplier is expected

    public T myMethod(){
        T object = makeNewThing().get(); // local object by calling supplier
        object.doStuff();
        return object;
    }
}

class CarService extends AbstractService<Car> {
    public Supplier<Car> makeNewThing(){
        return Car::new;
    }
}
Run Code Online (Sandbox Code Playgroud)

abstract class AbstractService<T extends SomeAbstractEntity> {
    protected T makeNewThing();  // object is expected, newness is assumed

    public T myMethod(){
        T object = makeNewThing(); // local object by calling constructor
        object.doStuff();
        return object;
    }
}

class CarService extends AbstractService<Car> {
    public Car makeNewThing(){
        return new Car();
    }
}
Run Code Online (Sandbox Code Playgroud)

我唯一能想到的是期望供应商确保创建一个新对象,但是当期望一个对象时,我们只能假设实现类正在调用构造函数而不是重用现有实例.

我想知道其他客观差异和可能的用例,如果有的话.提前致谢.

Era*_*ran 9

使用Supplier推迟创建实例.

这意味着您可以避免创建不必要的实例.

例如,假设您将输出传递makeNewThing()给某个方法.

public void makeNewThingSometimes (T newInstance)
{
    if (someCondition) {
        this.instance = newInstance;
    }
}

public void makeNewThingSometimes (Supplier<T> supplier)
{
    if (someCondition) {
        this.instance = supplier.get();
    }
}
Run Code Online (Sandbox Code Playgroud)

调用第一个变体需要创建一个T实例,即使您不打算使用它.

调用第二个变量仅在必要时创建T的实例.

使用a Consumer可以保存存储(如果创建实例需要大量内存)和时间(如果构造函数的执行是扩展的).


dav*_*xxx 5

我唯一能想到的是,期待一个供应商可以确保创建一个新对象,

不必要。
您可以Supplier通过以下方式实现:

 return SomeEntityImplementation::new;
Run Code Online (Sandbox Code Playgroud)

但你可以用其他方式实现它:

 if (myCachedObject != null){
    return (()-> myCachedObject);
 }
 return SomeEntityImplementation::new;
Run Code Online (Sandbox Code Playgroud)

两种方式都可以用于返回缓存的对象或创建新的对象。


优点之一是创建对象Supplier的情况:该对象实际上仅在调用方法时才创建。SupplierSupplier.get()

请注意,在您的示例中, usingSupplier不会带来任何优势,因为在两种情况下(有或没有Supplier),对象创建已经以惰性方式执行:在调用工厂方法时。
要利用它,您应该有一个提供Supplier<T> as 参数的方法,如 Eran 和 Dasblinkenlight 示例中所示。


另一个Supplier优点是它能够实现可能返回多个事物的工厂。
使用Supplier可以让代码更短、更易读,而且不依赖 Java Reflection。

假设你想从一个值创建对象Enum,你可以这样写:

public enum MyBaseClassFactory {

  ENUM_A (A::new),
  ENUM_B (B::new),
  ENUM_C (C::new),
  ENUM_D (D::new);

  private Supplier<BaseClass> supplier;

  MyBaseClassFactory (Supplier<BaseClass> supplier){
    this.supplier = supplier;
  }

  public BaseClass createObject(){
       return supplier.get();
  }
}
Run Code Online (Sandbox Code Playgroud)

你可以这样使用它:

BaseClass base = MyBaseClassFactory.ENUM_A.createObject();
Run Code Online (Sandbox Code Playgroud)

如果没有Supplier,您将不得不使用反射(可能在运行时失败)或编写冗长且难以维护的代码。

例如,使用反射:

public enum MyEnumFactoryClass {

    ENUM_A(A.class), ENUM_B(B.class), ENUM_C(C.class), ENUM_D(D.class);

    private Class<BaseClass> clazz;

    MyEnumFactoryClass(Class<BaseClass> clazz) {
       this.clazz = clazz;
    }

    public BaseClass createObject() {
       return clazz.newInstance();
    }

}
Run Code Online (Sandbox Code Playgroud)

例如,没有反射但有更详细的代码:

public enum MyEnumFactoryClass {

  ENUM_A {
     @Override
     public BaseClass createObject() {
        return new A();
     }
    },
    ENUM_B {
     @Override
     public BaseClass createObject() {
        return new B();
     }
    },
    ENUM_C {
    @Override
     public BaseClass createObject() {
        return new C();
     }
    },
    ENUM_D {
    @Override
     public BaseClass createObject() {
        return new D();
     }
    };
    public abstract BaseClass createObject();

}
Run Code Online (Sandbox Code Playgroud)

您当然可以通过将Supplier它与Map<String, Supplier<BaseClass>>.