【问题标题】:Map: Defining a Method for Type Integer and Double but not String映射:为 Integer 和 Double 但不是 String 类型定义方法
【发布时间】:2017-03-15 17:14:21
【问题描述】:

我正在尝试为我的新 Map 类定义一个方法 putIfGreaterThan()仅当新值大于旧值时,它才会用新值替换旧值 em>)。

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

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

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

【问题讨论】:

  • 你可以用? extends Number或类似的东西做一些事情;我会投票给回答者。现在要修复错误,所以不能处理这个问题。
  • 你不能使用Number,因为DoubleInteger是从Number扩展而来的吗?

标签: java generics parameters hashmap


【解决方案1】:

您可以使用装饰器模式并将泛型限制为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 的其他子类型,即抛出异常,只要你认为合适。

【讨论】:

  • 嗯,答案越来越好了。
【解决方案2】:

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

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

}

【讨论】:

    【解决方案3】:

    理想情况下,您希望声明一个引入类型参数 extends V &amp; Comparable&lt;? super V&gt; 的方法,但这不是有效的 Java。

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

    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&lt;? super V&gt; 的情况下,类型需要不同。如果两个构造函数具有相同的擦除,则可能是时候有意义地命名静态创建方法了。

    【讨论】:

      猜你喜欢
      • 2013-06-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-21
      • 1970-01-01
      • 1970-01-01
      • 2020-10-30
      • 1970-01-01
      相关资源
      最近更新 更多