Map:定义类型整数和双精度但不是字符串的方法

时间:2017-03-14 16:18:36

标签: java generics parameters hashmap

我正在尝试为我的新putIfGreaterThan()类定义一个方法Map给定一个键,只有当新值大于该值时,它才会将旧值替换为新值旧价值)。

我知道我可以通过合成(在新类中使用private final Map<String, Double> map;然后将Map传递给构造函数)或在新类中实现Map接口来实现此目的并将Map传递给构造函数(虽然我不确定哪种方法更优秀)。

我的主要问题是我需要能够在putIfGreaterThan()<String, Double>上调用方法<String, Integer>,而不能在<String, String>上调用(因为它没有意义在<String, String>上调用它。如果我使用泛型(<K, V>),客户端可以传递<String, String>,这是不允许的。另一方面,如果我允许Double,我将无法通过Integer,反之亦然。如何定义允许Integer或Double但不允许String的方法?

注意:我无法定义两个构造函数(一个用于Double,一个用于Integer)因为我得到错误:方法XX的擦除与XX类型中的另一个方法相同。

3 个答案:

答案 0 :(得分:2)

您可以使用装饰器模式并将泛型限制为Number的子类型,您可以通过this answer中的小技巧进行比较而不进行比较。它接受数字实例的字符串表示并创建BigDecimal的实例 - 从而绕过任何类型的转换。

下面你会找到装饰器的相关实现细节,当然你需要覆盖Map接口的其余方法。

public class GreaterThanDecorator<K, V extends  Number> implements Map<K, V> {

    private final Map<K, V> delegate;

    public GreaterThanDecorator(Map<K, V> delegate) {
        this.delegate = delegate;
    }

    public V putIfGreaterThan(K key, V value) {
        V old = delegate.get(key);
        if (old == null) {
            delegate.put(key, value);
            return null;
        }

        BigDecimal newValue = new BigDecimal(value.toString());
        BigDecimal oldValue = new BigDecimal(old.toString());

        if (newValue.compareTo(oldValue) >= 1)
            old = delegate.put(key, value);

        return old;
    }

} 

随意禁止Number的其他子类型,即通过抛出异常,视情况而定。

答案 1 :(得分:0)

遗憾的是,泛型和数字并不能很好地结合在一起。但你可以这样做:

public class MyMap {
    private final Map<String, Number> map = new HashMap<>();

    public void putInt(String key, int value) {
        map.put(key, value);
    }

    public void putDouble(String key, int value) {
        map.put(key, value);
    }

    public void putIfGreaterThan(String key, Number value) {
        if (value instanceof Double) {
            double doubleValue = (Double) value;
            map.compute(key, (k, v) -> {
                if (!(v instanceof Double) || v.doubleValue() > doubleValue) {
                    return v;
                } else {
                    return value
                }
            });
        } else if (value instanceof Integer) {
            int intValue = (Integer) value;
            map.compute(key, (k, v) -> {
                if (!(v instanceof Integer) || v.intValue() > intValue) {
                    return v;
                } else {
                    return value
                }
            });
        } else {
            throw new IllegalArgumentException("Expected Double or Integer, but got " + value);
        }
    }

}

答案 2 :(得分:0)

理想情况下,您需要声明一个引入类型参数extends V & Comparable<? super V>的方法,但该方法不是有效的Java。

但是,您可以在不使用&的情况下定义静态方法。

public static <K, V extends Comparable<? super V>> void putIfGreater(
    Map<K,V> map, K key, V value
) {
    V old = map.get(key);
    if (old != null && value.compareTo(old) > 0) {
        map.put(key, value);
    }

    // Or: 
    //map.computeIfPresent(key, (k, old) -> value.compareTo(old) > 0 ? value : old);
}

如果您沿着不同构造的路线走下去,那么VComparable<? super V>的情况下类型需要不同。如果两个构造函数具有相同的擦除,那么可能是时候有意义地命名静态创建方法了。