【问题标题】:Generics, type erasure and static fabric method泛型、类型擦除和静态结构方法
【发布时间】:2016-07-08 11:48:44
【问题描述】:

例如,我有 SimpleCallback 类

public class SimpleCallback<T> implements Callback<T> {

    private SuccessListener<T> successListener;
    private ErrorListener errorListener;

    protected SimpleCallback() {

    }

    public static <C> SimpleCallback<C> success(SuccessListener<C> listener) {
        SimpleCallback<C> callback = new SimpleCallback<>();
        callback.successListener = listener;
        return callback;
    }

    public SimpleCallback<T> error(ErrorListener errorListener) {
        this.errorListener = errorListener;
        return this;
    }

    @Override
    public void onComplete(T result) {
        notifySuccess(result);
    }

    @Override
    public void onError() {
        notifyError();
    }

    public interface SuccessListener<T> {

        void onSuccess(T result);
    }

    public interface ErrorListener {

        void onError();
    }

}

现在我想使用这个回调来异步获取猫:

SimpleCallback<List<Cat>> callback = SimpleCallback
            .success(cats -> cats.forEach(Cat::meow));

现在可以了,但是当我想添加错误监听器时,我的猫变成了原始对象

SimpleCallback<List<Cat>> callback = SimpleCallback
            .success(cats -> cats.forEach(Cat::meow)) <-- Here cats become objects
            .error(() -> System.out.println("Cats error"));

其中一种解决方案使用显式泛型:

SimpleCallback<List<Cat>> callback = SimpleCallback.<List<Cat>>
            .success(cats -> cats.forEach(Cat::meow))
            .error(() -> System.out.println("Cats error"));

但它看起来有点难看。那么有什么方法可以在没有显式泛型的情况下创建回调?

UPD:我认为@Jesper 提供了很好的解决方案

另一种解决方案是显式提供 lambda 表达式的参数类型:

.success((List<Cat> cats) -> cats.forEach(Cat::meow))

【问题讨论】:

  • 另一种解决方案是显式提供 lambda 表达式的参数类型:.success((List&lt;Cat&gt; cats) -&gt; cats.forEach(Cat::meow))
  • 请说明问题所在。如果只是它看起来不熟悉,那么我会说:习惯它比使用带有所有缺点的原始类型要好得多......
  • @martinhh 问题是关于类型推断:为什么在调用.success(...) 之后添加.error(...) 时,类型推断不再起作用(现在需要显式指定类型) .
  • 举个更简单的例子,class Box&lt;T&gt; { T t; public Box&lt;T&gt; set(T t) { this.t = t; return this; } public T get() { return t;}} 类显示了同样的问题。 String s = new Box&lt;String&gt;().set("Hello").get();Box&lt;String&gt; b = new Box&lt;&gt;(); String s = b.set("Hello").get(); 有效,但 String s = new Box&lt;&gt;().set("Hello").get() 无效。我认为这与&lt;&gt; 如何解析为一种类型有关。也许这为寻找答案提供了方向?

标签: java generics type-erasure


【解决方案1】:

显然,擦除泛型类型变量的策略会随上下文而变化。 当然,这必须有规则,但我不知道,抱歉。

从实用的角度来看,Jesper 建议的解决方案,即在成功方法中提供类型信息对我来说似乎完全可以接受。

.success((List&lt;Cat&gt; cats) -&gt; cats.forEach(Cat::meow))

否则可以在一个方法中同时传递两个监听器:

public static <C> SimpleCallback<C> create(
    SuccessListener<C> successListener, ErrorListener errorListener) {
    SimpleCallback<C> callback = new SimpleCallback<>();
    callback.successListener = successListener;
    callback.errorListener = errorListener;

    return callback;
}

并在一个步骤中创建回调:

Callback<List<Cat>> callback = SimpleCallback
        .create(
            cats -> cats.forEach(Cat::meow),
            () -> System.out.println("Cats error")
         );

【讨论】:

    【解决方案2】:

    简而言之,没有。从this answer about how the diamond operator functions in Java 1.8 开始,任何链式方法调用都存在类型推断问题。你的例子很相似,因为

    SimpleCallback<List<Cat>> callback = SimpleCallback
            .success(cats -> cats.forEach(Cat::meow));
    

    success 的参数推断C 的类型。

    因此您的选择是:

    • 显式声明泛型类型
    • 在使用链式调用之前获取命名引用(具有显式声明的泛型类型)

    对于您的示例,我更喜欢使用以下范例:

    公共类 SimpleCallback 实现回调 {

    private SuccessListener<T> successListener;
    private ErrorListener errorListener;
    
    protected SimpleCallback() {
    
    }
    
    public static <C> SimpleCallback<C> create() {
        return new SimpleCallback<>();
    }
    
    public SimpleCallback<T> withSuccessListener(SuccessListener<T> listener) {
        this.successListener = listener;
        return this;
    }
    
    public SimpleCallback<T> withErrorListener(ErrorListener errorListener) {
        this.errorListener = errorListener;
        return this;
    }
    
    @Override
    public void onComplete(T result) {
        notifySuccess(result);
    }
    
    @Override
    public void onError() {
        notifyError();
    }
    
    public interface SuccessListener<T> {
    
        void onSuccess(T result);
    }
    
    public interface ErrorListener {
    
        void onError();
    }
    

    }

    然后在一行中创建,在下一行添加和链接。

    SimpleCallback<List<Cat>> callback = SimpleCallback.create();
    callback = callback.success(cats -> cats.forEach(Cat::meow))
                       .error(() -> System.out.println("Cats error"));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-04
      • 1970-01-01
      相关资源
      最近更新 更多