This gist that I wrote 很好地解决了这个问题。
根据siegi 对Atreys 的回答的建议,我编写了一个构造函数,它找到“最近的共同祖先”(NCA)类并使用该类来创建数组。如果检查 null 并且提供的 Collection 的长度为 0 或全部为 null,则默认类型为 Object。它完全忽略了接口。
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import java.lang.reflect.Array;
import java.util.Iterator;
public class FDatum<T> {
public T[] coordinates;
// magic number is initial size -- assume <= 5 different classes in coordinates
public transient HashSet<Class> classes = new HashSet<Class>(5);
public FDatum (Collection<T> coordinates) {
// to convert a generic collection to a (sort of) generic array,
// we need to bend the rules:
// 1. default class T is Object
// 2. loop over elements in Collection, recording each unique class:
// a. if Collection has length 0, or
// if all elements are null, class T is Object
// b. otherwise, find most specific common superclass, which is T
// record all unique classes in coordinates
for (T t : coordinates) this.classes.add(t.getClass());
// convert to list so we can easily compare elements
List<Class> classes = new ArrayList<Class>(this.classes);
// nearest common ancestor class (Object by default)
Class NCA = Object.class;
// set NCA to class of first non-null object (if it exists)
for (int ii = 0; ii < classes.size(); ++ii) {
Class c = classes.get(ii);
if (c == null) continue;
NCA = c; break;
}
// if NCA is not Object, find more specific subclass of Object
if (!NCA.equals(Object.class)) {
for (int ii = 0; ii < classes.size(); ++ii) {
Class c = classes.get(ii);
if (c == null) continue;
// print types of all elements for debugging
System.out.println(c);
// if NCA is not assignable from c,
// it means that c is not a subclass of NCA
// if that is the case, we need to "bump up" NCA
// until it *is* a superclass of c
while (!NCA.isAssignableFrom(c))
NCA = NCA.getSuperclass();
}
}
// nearest common ancestor class
System.out.println("NCA: " + NCA);
// create generic array with class == NCA
T[] coords = (T[]) Array.newInstance(NCA, coordinates.size());
// convert coordinates to an array so we can loop over them
ArrayList<T> coordslist = new ArrayList<T>(coordinates);
// assign, and we're done!
for (int ii = 0; ii < coordslist.size(); ++ii)
coords[ii] = coordslist.get(ii);
// that's it!
this.coordinates = coords;
}
public FDatum (T[] coordinates) {
this.coordinates = coordinates;
}
}
以下是在 jshell 中使用它的一些示例(为简洁起见,删除了“未检查”类警告):
jshell> FDatum d = new FDatum(new ArrayList(Arrays.asList((double)1, (Double)3.3)))
class java.lang.Double
NCA: class java.lang.Double
d ==> com.nibrt.fractal.FDatum@9660f4e
jshell> d.coordinates
$12 ==> Double[2] { 1.0, 3.3 }
jshell> d = new FDatum(new ArrayList(Arrays.asList((double)1, (Double)3.3, (byte)7)))
class java.lang.Byte
class java.lang.Double
NCA: class java.lang.Number
d ==> com.nibrt.fractal.FDatum@6c49835d
jshell> d.coordinates
$14 ==> Number[3] { 1.0, 3.3, 7 }
jshell> d = new FDatum(new ArrayList(Arrays.asList((double)1, (Double)3.3, (byte)7, "foo")))
class java.lang.Byte
class java.lang.Double
class java.lang.String
NCA: class java.lang.Object
d ==> com.nibrt.fractal.FDatum@67205a84
jshell> d.coordinates
$16 ==> Object[4] { 1.0, 3.3, 7, "foo" }