简体   繁体   English

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

[英]Java: Instantiate a generic type by returning a Supplier vs returning a new instance

I was reading how to instantiate a generic and after reading and applying this answer ; 我正在阅读如何实例化一个通用的,并在阅读并应用这个答案之后 ; I would like to know what would be the differences between expecting a Supplier<T> vs. expecting a new instance of T . 我想知道期待Supplier<T>与期望新的T实例之间的区别。

Example: 例:

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;
    }
}

vs.

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();
    }
}

The only thing I can think of is that expecting a supplier ensures that a new object will be created, but when expecting an object we can only assume that the implementing classes are calling the constructor and not re-using an existing instance. 我唯一能想到的是期望供应商确保创建一个新对象,但是当期望一个对象时,我们只能假设实现类正在调用构造函数而不是重用现有实例。

I'd like to know of other objective differences and possible use cases , if any. 我想知道其他客观差异和可能的用例 ,如果有的话。 Thanks in advance. 提前致谢。

Using a Supplier postpones the creation of the instance. 使用Supplier商会推迟创建实例。

This means that you might avoid a creation of an unnecessary instance. 这意味着您可以避免创建不必要的实例。

For example, suppose you pass the output of makeNewThing() to some method. 例如,假设您将makeNewThing()的输出传递给某个方法。

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

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

Calling the first variant requires creating an instance of T even if you are not going to use it. 调用第一个变体需要创建一个T实例,即使您不打算使用它。

Calling the second variant only creates an instance of T when necessary. 调用第二个变量仅在必要时创建T的实例。

Using a Consumer can save both storage (if the create instance requires a significant amount of memory) and time (if the execution of the constructor is expansive). 使用Consumer可以保存存储(如果create实例需要大量内存)和时间(如果构造函数的执行是扩展的)。

The only thing I can think of is that expecting a supplier ensures that a new object will be created, 我唯一能想到的是期望供应商确保创建一个新对象,

Not necessarily. 不必要。
You implement the Supplier in this way : 您以这种方式实施Supplier

 return SomeEntityImplementation::new;

But you could have implemented it in this other way : 但你可以用另一种方式实现它:

 if (myCachedObject != null){
    return (()-> myCachedObject);
 }
 return SomeEntityImplementation::new;

Both ways may be used to return a cached object or create a new one. 两种方式都可用于返回缓存对象或创建新对象。


One of Supplier advantages is the case of Supplier creating an object : this one is actually created only as the Supplier.get() method is invoked. Supplier优势之一是Supplier创建对象的情况:实际上只有在调用Supplier.get()方法时才创建此对象。

Note that in your example, using Supplier doesn't bring any advantage as in both cases (with or without Supplier ) the object creation is already performed in a lazy way : as the factory method is invoked. 请注意,在您的示例中,使用“ Supplier并没有带来任何好处,因为在两种情况下(使用或不使用“ Supplier )都会以惰性方式执行对象创建:调用工厂方法。
To take advantage of it, you should have a method that provides a Supplier<T> as parameter as in the Eran and Dasblinkenlight examples. 要利用它,您应该有一个方法,提供Supplier<T>作为参数,如Eran和Dasblinkenlight示例中所示。


Another Supplier advantage is its ability to implement factory that may return multiple of things. 另一个Supplier优势是它能够实现可能返回多项内容的工厂。
Using Supplier allows to have a shorter and more readable code and besides that doesn't rely on Java Reflection. 使用Supplier允许使用更短且更易读的代码,此外不依赖于Java Reflection。

Supposing that you want to create the object from an Enum value, you could so write : 假设您要从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();
  }
}

You could so use it : 你可以这样使用它:

BaseClass base = MyBaseClassFactory.ENUM_A.createObject();

Without Supplier , you will have to use Reflection (that may fail at runtime) or write a verbose and unmaintainable code. 如果没有Supplier ,您将不得不使用Reflection(可能在运行时失败)或编写详细且不可维护的代码。

For example with Reflection : 例如使用Reflection:

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();
    }

}

For example without reflection but with more verbose code : 例如,没有反射,但有更详细的代码:

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();

}

You could of course take advantage in a close way of Supplier by using it with a Map<String, Supplier<BaseClass>> . 当然,您可以通过与Map<String, Supplier<BaseClass>>一起使用它来与Supplier密切合作。

The first solution is more flexible, because an extra level of indirection in object creation lets users of your class library change the source of new items independently of ServiceImpl<SomeEntityImplementation> class. 第一个解决方案更灵活,因为在对象创建中额外的间接级别允许类库的用户独立于ServiceImpl<SomeEntityImplementation>类更改新项的源。

You can make a new Supplier<T> instance without subclassing or recompiling ServiceImpl , because there is an extra level of indirection. 您可以在不进行子类化或重新编译ServiceImpl情况下创建新的Supplier<T>实例,因为存在额外的间接级别。 ServiceImpl could be implemented as follows: ServiceImpl可以实现如下:

class ServiceImpl<SomeEntityImplementation> {
    private final Supplier<SomeEntityImplementation> supplier;
    public Supplier<T> makeNewThing(){
        return supplier;
    }
    public ServiceImpl(Supplier<SomeEntityImplementation> s) {
        supplier = s;
    }
}

This makes it possible for users of ServiceImpl to provide their own Supplier<T> , which is not possible using the second approach, in which the source of new items is merged into the implementation of service itself. 这使得ServiceImpl用户可以提供他们自己的Supplier<T> ,这是使用第二种方法无法实现的,其中新项目的来源被合并到服务本身的实现中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM