【问题标题】:how to parameterize static fields in Java generics based on type如何根据类型参数化Java泛型中的静态字段
【发布时间】:2014-03-03 00:25:44
【问题描述】:

据我所知,Java 泛型中的静态字段在所有实例之间共享,而与类型参数无关。比如在下面的类中

class A<T> {
    static String s;
}

字符串 s 将在 A 的所有实例之间共享,而与 T 无关。

我想参数化 T 上的静态字段(和相关的一次性操作),以便在上面的示例中,String s 在 A、A 和以此类推。

到目前为止,我能够提出以下解决方案:

static class Rec {
    private Class<?> clazz;
    private String name;

    public Rec(Class<?> clazz, String name) {
        this.clazz = clazz;
        // parameterized, quasi-static field
        this.name = name;
    }

    protected Class<?> ofClass() { return clazz; }
    protected String ofName() { return name; }
}

static abstract class Base<T> {
    private static List<Rec> derived = new ArrayList<Rec>();
    private Class<T> type;

    public Base(Class<T> type, String s) {
        this.type = type;
        for (Rec rec : derived) {
            if (rec.ofClass() == type) return;
        }
        Rec rec = new Rec(type, type.getName());
        derived.add(rec);
        System.out.println("Added " + rec.ofName());
    }

    protected String getName() {
        for (Rec rec : derived)
            if (rec.ofClass() == this.type) return rec.ofName();
        return null;
    }
}

static class Test1 extends Base<Test1> {
    public Test1() {
        super(Test1.class, Test1.class.getName());
    }
}

static class Test2 extends Base<Test2> {
    public Test2() {
        super(Test2.class, Test2.class.getName());
    }
}

Test1 t1 = new Test1();
System.out.println(t1.getName());

Test2 t2 = new Test2();
System.out.println(t2.getName());

Test2 t3 = new Test2();
System.out.println(t3.getName());

此代码在基类的每个派生类型记录列表中创建准静态字段的单独实例。因此,两个单独的类名“Test1”和“Test2”被打印到控制台;但是,t2 和 t3 实例共享相同的名称字段。

虽然基类在编译时不知道它的派生类,但它是在运行时,这可能并不像应有的那样干净。此外,每次调用使用参数化准静态字段的基方法时,都会对列表进行线性搜索以找到对应的类型。

有没有更有效、更惯用的方式来做到这一点?

【问题讨论】:

  • 为什么不使用地图而不是列表来避免线性搜索?

标签: java generics static field


【解决方案1】:

我不知道你为什么要以这种方式共享字段,但只要你将自己限制为 reifiable 类型,你就可以像这样创建一个静态地图:

public abstract class Base< T > {
    private static final Map< ParameterizedType, String > REIFIED_LOCAL = new ConcurrentHashMap<>();

    public Base() {
        addEntryIfNecessary(
            ( ParameterizedType )getClass().getGenericSuperClass()
        );
    }

    private synchronized static void addEntryIfNecessary( ParameterizedType pt ) {
        if ( pt instanceof Class && !REIFIED_LOCAL.containsKey( pt ) )
            REIFIED_LOCAL.put( pt, pt.getActualTypeArguments()[ 0 ].toString();
    }

    protected String getName() {
        return REIFIED_LOCAL.get( ( ParameterizedType )getClass().getGenericSuperClass() );
    }
}

【讨论】:

  • 在这种情况下你可以使用静态初始化器而不是构造器吗?
  • @LeonardBrünings,在这种情况下我会为pt 使用什么类对象?我们需要在每个具体的完全参数化的子类中使用初始化程序。不如 IMO 好
  • 但是这样你会为每个新实例产生同步成本。
  • 每次为每个特定的实际类型参数创建第一个新实例时,OP 希望有一个条目进入此映射。如果不为每个新实例反思性地检查该参数,无论是否删除,都无法实现此目标。必须付钱给吹笛者。
  • 第一次是的,这就是为什么我建议在扩展类中使用静态初始化器。
猜你喜欢
  • 2015-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-14
  • 1970-01-01
  • 1970-01-01
  • 2012-03-27
  • 1970-01-01
相关资源
最近更新 更多