Java中破坏的逆变边界的安全解决方法?

时间:2015-08-03 20:40:24

标签: java generics

正如Bounding generics with 'super' keyword中所讨论的,当涉及方法泛型的下限时,Java类型系统被破坏/不完整。由于Optional现在是JDK的一部分,我开始更多地使用它,并且Guava遇到的问题与他的可选性开始变得困难。我提出了一个体面的工作,但我不确定它是否安全。首先,让我设置一个例子:

public class A {}
public class B extends A {}

我希望能够声明一个方法:

public class Cache {
   private final Map<String, B> cache;
   public <T super B> Optional<T> find(String s) {
       return Optional<T>.ofNullable(cache.get(s));
   }
}

以下两项工作都是:

A a = cache.find("A").orElse(new A())
B b = cache.find("B").orElse(new B())

作为一种解决方法,我有一个静态实用方法如下:

public static <S, T extends S> Optional<S> convertOptional(Optional<T> optional) {
    return (Optional<S>)optional;
} 

所以我的最后一个问题是,这是否与上面的“理想”代码类型安全?

A a = OptionalUtil.<A,B>convertOptional(cache.find("A")).orElse(new A());

3 个答案:

答案 0 :(得分:5)

您有效地尝试查看Optional<B>作为Optional<A>返回而不更改返回类型(因为您无法使用super)。我只会map identity function

A a = cache.find("A").map(Function.<A> identity()).orElse(new A());
// or shorter
A a = cache.find("A").<A> map(x -> x).orElse(new A());

我认为你的方法没有任何问题。

答案 1 :(得分:2)

是的,您的代码是类型安全的,因为您将Optional<T>强制转换为Optional<S>,而T始终是S.您可以使用@SuppressWarnings("unchecked")注释向编译器指明此代码你的convertOptional实用工具方法。

除了其他优秀的答案,你也可以这样做。请注意,与第一个版本相比,缺少泛型。

package com.company;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

public class Cache {
    private static final Function<A,A> CAST_TO_A = Function.<A>identity();
    private final Map<String, B> cache = new HashMap<>();

    public Optional<B> find(String s) {
        return Optional.ofNullable(cache.get(s));
    }

    public static void main(String[] args) {
        Cache cache = new Cache();

        Optional<B> potentialA = cache.find("A");

        A a = potentialA.isPresent() ? potentialA.get() : new A();

        A anotherWay = cache.find("A").map(CAST_TO_A).orElse(new A());

        B b = cache.find("B").orElse(new B());
    }

    public static class A {}
    public static class B extends A {}
}

答案 2 :(得分:1)

在实践中,我认为它是类型安全的,因为Optional类是不可变的,T是S的子类型。

请注意,虽然T is subtype of S NOT 表示Optional<T> is subtype of Optional<S>,但从理论上讲,施法不正确。在某些情况下,进行此类演员表并不安全,并且在运行时可能会出现问题(与List一样)。

所以我的建议是尽可能避免强制转换,我宁愿定义像getOrElse这样的方法。此外,为了完整起见,方法convertOptional中的泛型类型可以简化如下。

class OptionalUtil {
    public static <S> S getOrElse(Optional<? extends S> optional, Supplier<? extends S> other) {
        return optional.map(Function.<S>identity()).orElseGet(other);
    }

    public static <S> Optional<S> convertOptional(Optional<? extends S> optional) {
        return (Optional<S>)optional;
    }
}

他们可以这样使用:

A a1 = OptionalUtil.getOrElse(cache.find("A"), A::new);
A a2 = OptionalUtil.<A>convertOptional(cache.find("A")).orElseGet(A::new);

[编辑] 我将方法orElse替换为orElseGet,因为前者会创建一个新的A对象,即使{{1}存在。