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

时间:2017-11-13 12:40:28

标签: java generics java-8

我正在阅读如何在阅读和应用this answer之后实例化一个通用的;我想知道期待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;
    }
}

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

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

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

3 个答案:

答案 0 :(得分:9)

使用 protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ValidUserAuthorization requirement) { List<KeyValuePair<string, string>> parameters = new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("UserAlias", context.User.Identity.Name.Split('@')[0]) }; var isAuthorized = this.CubeServiceManager.GetDataFromService("CheckIfAuthorized", "GET", this.ConnectionStrings.BaseAddress, parameters); if (isAuthorized == "true") { context.Succeed(requirement); } return Task.CompletedTask; } 推迟创建实例。

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

例如,假设您将Supplier的输出传递给某个方法。

makeNewThing()

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

调用第二个变体只在必要时创建一个T实例。

使用public void makeNewThingSometimes (T newInstance) { if (someCondition) { this.instance = newInstance; } } public void makeNewThingSometimes (Supplier<T> supplier) { if (someCondition) { this.instance = supplier.get(); } } 可以节省存储空间(如果创建实例需要大量内存)和时间(如果构造函数的执行是扩展的)。

答案 1 :(得分:4)

  

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

不一定 您以这种方式实施Supplier

 return SomeEntityImplementation::new;

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

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

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

Supplier优势之一是Supplier创建对象的情况:实际上只有在调用Supplier.get()方法时才创建此对象。

请注意,在您的示例中,使用Supplier并没有带来任何好处,因为在两种情况下(有或没有Supplier),对象创建已经以惰性方式执行:作为工厂方法被调用。
要利用它,您应该有一个方法,提供{E}和Dasblinkenlight示例中的Supplier<T>参数。

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

你可以这样使用它:

BaseClass base = MyBaseClassFactory.ENUM_A.createObject();

如果没有Supplier,则必须使用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();
    }

}

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

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

}

当然,您可以通过SupplierMap<String, Supplier<BaseClass>>一起使用{{1}}。{/ p>

答案 2 :(得分:3)

第一个解决方案更灵活,因为在对象创建中额外的间接级别允许类库的用户独立于ServiceImpl<SomeEntityImplementation>类更改新项目的来源。

您可以在不进行子类化或重新编译Supplier<T>的情况下创建新的ServiceImpl实例,因为存在额外的间接级别。 ServiceImpl可以按如下方式实施:

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

这使得ServiceImpl的用户可以提供他们自己的Supplier<T>,这是使用第二种方法无法实现的,其中新项目的来源被合并到服务本身的实现中。