泛型,类型擦除和静态结构方法

时间:2016-07-08 11:48:44

标签: java generics type-erasure

例如,我有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))

2 个答案:

答案 0 :(得分:0)

显然,擦除泛型类型变量的策略随上下文而变化。 当然必须有规则如何运作,但我不知道,对不起。

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

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

否则,可以通过一种方法传递两个Listener:

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

答案 1 :(得分:0)

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

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

C的参数中推断出类型success

因此您的选择是:

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

对于您的示例,我更倾向于使用以下范例:

public class SimpleCallback实现了Callback {

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