作者热门文章
- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我的目标是创建一个基于 Java 集合的 1 对 n 容器类。 (大致类似于 Map<String,Collection<Double>>
——对于单个字符串,映射可以包含 n 个 double 值。例如,对于键“坐标”,容器可以包含值 [3.1、4.3、7.2])。
一个必需的特性是可以自由定义每个键包含 n 个元素的类型。因此,上面示例中的 Collection 应该可以替换为 List 或 Set。
以下必须是可能的,如果是,目标就达到了:
OneToN2<String, Double, List<Double>> cl =
new OneToN2<>( ArrayList.class );
OneToN2<String, Double, Set<Double>> cs =
new OneToN2<>( HashSet.class );
现在我从以下代码开始:
package com.fun.with.generics;
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.HashMap;
/**
* A map that maps a single key to n values.
*
* @param <K> The key type.
* @param <V> The value type.
* @param <V> The type used as container for the to-n elements.
*/
@SuppressWarnings("serial")
public class OneToN2<K,V,CT extends Collection<V> > extends HashMap<K, Collection<V>>
{
private final Constructor<CT> _containerCtor;
/**
* Create an instance.
*/
public OneToN2( Class<CT> claß )
{
try
{
_containerCtor = claß.getConstructor();
}
catch ( Exception e )
{
throw new IllegalArgumentException( "Need default ctor." );
}
}
/**
* Add a single key value pair.
*
* @param key The key.
* @param element The element to add.
*/
synchronized public void add( K key, V element )
{
Collection<V> ct = get( key );
if ( ct == null )
{
ct = makeToN();
put( key, ct );
}
ct.add( element );
}
/**
* Add an element for multiple keys.
*
* @param keys The keys to add.
* @param element The element the keys refer to.
*/
synchronized public void add( Collection<K> keys, V element )
{
for ( K c : keys )
add( c, element );
}
/**
* Get a newly allocated container holding the n values.
*/
@Override
public Collection<V> get( Object key )
{
Collection<V> n =
super.get( key );
Collection<V> result =
makeToN();
result.addAll( n );
return result;
}
/**
* Create a new instance of the to-n container.
*/
private Collection<V> makeToN()
{
try
{
return _containerCtor.newInstance();
}
catch ( Exception e )
{
throw new InternalError( "Could not create to-n collection." );
}
}
}
请注意,我只保留了最少的一组操作来展示我的意图。
到目前为止一切顺利:我能够在技术上表达我的意图并且编译器接受了它,所以第一步成功了。
现在问题来了:我无法创建上述类型的实例 :)
以下示例用例显示了预期用途,但它们都不起作用:
预期的解决方案是:
// Declaration (this compiles):
OneToN2<String, Double, List<Double>> x;
// Initialisation (does not compile). Error is Type mismatch: cannot
// convert from OneToN2<String,Double,ArrayList>
// to OneToN2<String,Double,List<Double>>
x = new One2N2<>( ArrayList.class );
真可惜,但我知道编译器无法推断出 ArrayList.class 是一个列表。所以我尝试了不太理想但可以接受的替代方案:
// Declaration (this compiles):
OneToN2<String, Double, ArrayList<Double>> x;
// Initialisation (does not compile). Error is Type mismatch: cannot
// convert from OneToN2<String,Double,ArrayList>
// to OneToN2<String,Double,ArrayList<Double>>
x = new One2N2<>( ArrayList.class );
又一次令人失望,或多或少不断出现错误消息。
问题是:我哪里出错了?
(我为什么要那个?学术兴趣,泛型的乐趣。但老实说,我在几个系统中创建了类似的类型,导致代码重复,因为 to-n 容器是固定的。为了防止这种情况,我正在寻找一个通用实现。)
最佳答案
如果您使用的是 Java 8,Supplier
将为您清理一切。
public class OneToN2<K,V, CT extends Collection<V> > extends HashMap<K, Collection<V>>
{
private final Supplier<CT> _containerCtor;
/**
* Create an instance.
*/
public OneToN2( Supplier<CT> _containerCtor )
{
this._containerCtor = _containerCtor;
}
...
/**
* Create a new instance of the to-n container.
*/
private Collection<V> makeToN()
{
return _containerCtor.get();
}
}
然后你可以像这样实例化你的集合:
public static void main(String[] args) {
OneToN2<String, Double, List<Double>> x = new OneToN2<>(ArrayList::new);
}
关于Java 泛型 : How to create a to-n collection with configurable value holder type,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40780757/
我是一名优秀的程序员,十分优秀!