如何存储Java类型,只允许某些特定类型?

时间:2011-09-07 19:10:00

标签: java generics

让我们假设我想要一个作为记录描述符的类,其中每个记录都有一组属性。

每个属性都有一个唯一的名称,并且应该具有与某个Java类型相对应的特定类型,例如Integer,String,Short,Double,......

应该限制可能的类型列表,例如我只支持Integer和String。

private HashMap<String, Class> attributeList = new HashMap<String, Class>();

在上面的例子中,HashMap是一个属性列表,其中key是属性名称,值应该是属性的类型(Integer或String)。

限制Hashmap值定义的最佳方法是什么?

4 个答案:

答案 0 :(得分:10)

您当然可以使用包装器方法向地图添加元素,并检查Integer和Sring。但是你只会遇到运行时错误。我同意你的观点,限制类型以获得静态错误要好得多。

为此,我实际上不会使用Integer.class和String.class,而是使用枚举:

enum Types { String, Integer };

private Map<String, Types> attributeList = new HashMap<String, Types>();

更新:

想想看,还有另一个(但更复杂的)解决方案,如果你坚持Class对象:你可以使用伪枚举模式,即使用一组常量(通常使用整数2 ^ i)就像枚举一样。因此,您可以将Class对象定义为常量。当然,这并不能保证没有其他类对象被放入地图中。这就是为什么Joshua Bloch第30项说“使用枚举而不是int常量”。 但是然后您可以使用Checker Framework使用假枚举检查器在常量上拉出其他类型系统:

@SuppressWarnings("fenum:assignment.type.incompatible")
public class TypeEnum {
  public static final @Fenum("Types") Class INT_CONST = Integer.class;
  public static final @Fenum("Types") Class STR_CONST = String.class;
}

然后,您可以使用Class类型的限制来定义地图:

private HashMap<String, @Fenum("Types") Class> attributeList
   = new HashMap<String, @Fenum("Types") Class>();

当然,您需要将Fenum Checker包含在编译器中。

答案 1 :(得分:6)

如何使用HashMap子类并覆盖put方法,在使用不支持的类型时抛出异常? (未经测试......就在我的头顶。)

class MyAttributes extends HashMap<String, Class> {
    private Set<Class> allowedTypes;

    public MyAttributes() {
        allowedTypes = new HashSet<Class>();
        allowedTypes.add(Integer.class);
    }
    public Class put(String key, Class value) {
        if(!allowedTypes.contains(value)) {
            throw new UnsupportedTypeException(); // <-- Your new exception type.
        }
        return super.put(key, value);
    }
}

答案 2 :(得分:2)

在我看来,你有三个选择:

  1. 使用您拥有的定义,当您拉取值时,请检查它是否是正确类型之一。
  2. 对HashMap进行子类化,并在添加元素时强制执行所述子类中的类型限制。
  3. 拥有多个地图,每个地图对应您要允许的每种类型,并进行相应的输入。
  4. 每个选项都有优点和缺点,您应该使用哪一个应该根据您的使用方式来决定。

答案 3 :(得分:0)

方法超载怎么样?

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;

public class CarrierDate<T> {

    private T localDate;

    private CarrierDate(T localDate) {
        this.localDate = localDate;
    }

    public T getLocalDate() {
        return localDate;
    }

    public static CarrierDate<LocalDate> instance(LocalDate date) {
        return new CarrierDate<>(date);
    }

    public static CarrierDate<LocalDateTime> instance(LocalDateTime date) {
        return new CarrierDate<>(date);
    }

    public static CarrierDate<OffsetDateTime> instance(OffsetDateTime date) {
        return new CarrierDate<>(date);
    }
}