您可以将类型参数 <T> 限制为多个特定的 类
Can you restrict a type parameter <T> to multiple specific classes
我正在写一个泛型 class Bla
类型参数 T
.
我可以限制 T
,以便只能使用我想支持的 class 吗?
public class Bla<T> {
private T foo;
private Class<T> fooClazz;
}
我希望 Bla 支持大多数 原始类型 classes (Enum, Boolean, Integer, String, ...),还有我自己的接口 Supportable
.
public interface Supportable {
void doSpecific(Bla _bla);
}
Bla
有一个方法 do()
,它处理支持的 classes,或者如果使用了我不支持的 class 则抛出异常。
public void do() {
if (Enum.class.isAssignableFrom(fooClazz)) {
// Do Enum specific code
} else if (Boolean.class.isAssignableFrom(fooClazz)) {
// Do Boolean specific code
} else if (Integer.class.isAssignableFrom(fooClazz)) {
// Do Integer specific code
} else if (String.class.isAssignableFrom(fooClazz)) {
// Do String specific code
} else if (Supportable.class.isAssignableFrom(fooClazz)) {
((Supportable) foo).doSpecific();
} else {
throw new UnsupportedDataTypeException(fooClazz + "is not supported");
}
}
我知道我能做到。
public class Bla<T extends Number> {}
所以只能使用扩展Number
的classes,但是有这样的东西吗?
public class Bla<T extends Number | String> {}
那么 String
也是可能的吗?
我能想到的唯一解决方案是为不同的类型制作多个 Bla
classes。
public class BlaEnum {}
public class BlaBoolean {}
public class BlaInteger {}
public class BlaString {}
public class BlaSupportable {}
您想要支持的 classes 的通用超类型 — 考虑到您的 Supportable 类型 — 是 Object
但是,如果您将 class 定义为从 Object
(默认情况下)继承并实现 Supportable
,您无法通过这种方式限制通用 class。
如果我是你,我会使用工厂模式并编写一个接受任何事物(对象的子class)及其类型的方法来实例化 Bla
的相关实例。 Create 方法应该是通用的,但您不能在此处施加任何限制,只有当类型不是 acceptable 时您才可以抛出异常。我知道,这不是您可能期望的那种答案。但是没有其他办法(在你的情况下)。
P.S 对于那些认为OP做错事的人,我们不应该在现实世界中有这样的设计。我想介绍一个例子。假设您要编写一个 class,它假设创建一个 in-memory table(就像我们在数据库中所做的那样,但使用 java 数据类型)。您还想支持用户的数据类型!那你会怎么做呢?
限制它的一种方法是使用静态重载工厂方法来构造对象。
public class Bla<T> {
private final T foo;
private final Class<T> fooClazz;
private Bla(T foo, Class<T> fooClazz) { // Must be private
this.foo = foo;
this.fooClazz = fooClazz;
}
@SuppressWarnings("unchecked")
public static <E extends Enum<E>> Bla<E> of(E foo) { // Caveat: Cannot handle null
return new Bla<>(foo, (Class<E>) foo.getClass());
}
public static Bla<Boolean> of(Boolean foo) {
return new Bla<>(foo, Boolean.class);
}
public static Bla<Integer> of(Integer foo) {
return new Bla<>(foo, Integer.class);
}
public static Bla<String> of(String foo) {
return new Bla<>(foo, String.class);
}
public static Bla<Supportable> of(Supportable foo) {
return new Bla<>(foo, Supportable.class);
}
public void do() {
// ...
}
// ...
}
它改变了调用者构造实例的方式,但实际上也简化了它,因为调用者不必传入 Class<T>
,例如
// With constructor (old way)
Bla<MyEnum> e2 = new Bla<>(MyEnum.A, MyEnum.class);
Bla<Boolean> b2 = new Bla<>(true, Boolean.class);
Bla<Integer> i2 = new Bla<>(42, Integer.class);
Bla<String> s2 = new Bla<>("", String.class);
Bla<Supportable> su2 = new Bla<>(supportable, Supportable.class);
// With static factory method (new way)
Bla<MyEnum> e1 = Bla.of(MyEnum.A);
Bla<Boolean> b1 = Bla.of(true);
Bla<Integer> i1 = Bla.of(42);
Bla<String> s1 = Bla.of("");
Bla<Supportable> su1 = Bla.of(supportable);
// Unsupported types are not allowed
Bla<Double> i1 = Bla.of(3.14); // Error: The method of(E) in the type Bla is not applicable for the arguments (double)
但是,在 do()
方法中不应使用 multi-way if
语句,而应使用 sub类。 sub类 对调用者是隐藏的,所以它没有外部区别,但它消除了对 multi-way if
语句/switch
语句的需要:
public abstract class Bla<T> {
private final T foo;
private final Class<T> fooClazz;
private Bla(T foo, Class<T> fooClazz) { // Must be private
this.foo = foo;
this.fooClazz = fooClazz;
}
@SuppressWarnings("unchecked")
public static <E extends Enum<E>> Bla<E> of(E foo) { // Caveat: Cannot handle null
return new Bla<>(foo, (Class<E>) foo.getClass()) {
@Override
public void do() {
// Do Enum specific code
}
};
}
public static Bla<Boolean> of(Boolean foo) {
return new Bla<>(foo, Boolean.class) {
@Override
public void do() {
// Do Boolean specific code
}
};
}
public static Bla<Integer> of(Integer foo) {
return new Bla<>(foo, Integer.class) {
@Override
public void do() {
// Do Integer specific code
}
};
}
public static Bla<String> of(String foo) {
return new Bla<>(foo, String.class) {
@Override
public void do() {
// Do String specific code
}
};
}
public static Bla<Supportable> of(Supportable foo) {
return new Bla<>(foo, Supportable.class) {
@Override
public void do() {
foo.doSpecific(this);
}
};
}
public abstract void do(); // Is now abstract
// ...
}
您当然可以创建(私有)静态嵌套 类,或 (package-private) top-level 类,而不是匿名的 类,如果你愿意。
使用 sub类 允许在多个方法中执行 fooClass
特定操作。如果你只有一个方法,你可以使用 lambdas 表达式 and/or 方法引用代替:
public class Bla<T> {
private final T foo;
private final Class<T> fooClazz;
private final Consumer<Bla<T>> doImpl;
private Bla(T foo, Class<T> fooClazz, Consumer<Bla<T>> doImpl) { // Must be private
this.foo = foo;
this.fooClazz = fooClazz;
this.doImpl = doImpl;
}
@SuppressWarnings("unchecked")
public static <E extends Enum<E>> Bla<E> of(E foo) { // Caveat: Cannot handle null
return new Bla<>(foo, (Class<E>) foo.getClass(), bla -> {
// Do Enum specific code
});
}
public static Bla<Boolean> of(Boolean foo) {
return new Bla<>(foo, Boolean.class, bla -> {
// Do Boolean specific code
});
}
public static Bla<Integer> of(Integer foo) {
return new Bla<>(foo, Integer.class, bla -> {
// Do Integer specific code
});
}
public static Bla<String> of(String foo) {
return new Bla<>(foo, String.class, bla -> {
// Do String specific code
});
}
public static Bla<Supportable> of(Supportable foo) {
return new Bla<>(foo, Supportable.class, foo::doSpecific);
}
public void do() {
doImpl.accept(this);
}
// ...
}
所以要恢复我的进步。
在@Iman提示使用工厂模式后,我开始使用静态重载工厂方法来实现它。
这是我使用 Enum
、Boolean
和 Supportable
接口作为示例生成的代码。它类似于@Andreas 发布的代码。
public static <E extends Enum<E>> Bla<E> createBla(E id) {
return new Bla.EnumBla<E>((Class<E>) id.getClass()).setID(id);
}
public static Bla<Boolean> createBla(boolean id) {
return new Bla.BooleanBla().setID(id);
}
public static <S extends Supportable> Bla<S> createBla(S id) {
return new Bla.SupportableBla<S>((Class<S>) id.getClass()).setID(id);
}
我决定为我想要支持的类型创建 Bla
的静态子类。
public abstract class Bla<T> {
private T foo;
private Bla() {}
public T getFoo() { return foo; }
public Bla<T> setFoo(T foo) { this.foo = foo; return this; }
public abstract void do();
public static class EnumBla<E extends Enum<E>> extends Bla<E> {
private final Class<E> fooClazz;
public EnumBla(Class<E> fooClazz) { super(); this.fooClazz = fooClazz; }
@Override
protected void do() { // Do Enum specific code}
}
public static class BooleanBla extends Bla<Boolean> {
public BooleanBla() { super(); }
@Override
protected void do() { // Do Boolean specific code }
}
public static class SupportableBla<S extends Supportable> extends Bla<S> {
private final Class<S> fooClazz;
public SupportableBla(Class<S> fooClazz) { super(); this.fooClazz = fooClazz; }
@Override
protected void do() { if(super.id != null) super.id.doSpecific(this); }
}
}
我在 BooleanBla
中没有 fooClazz
,因为那里不需要它。
此外,我无法完全删除“嵌套”if 语句,因为我想提供在没有所需 foo 类型实例的情况下创建 Bla
的能力。
public static <C, E extends Enum<E>, S extends Supportable> Bla<C> createBla(Class<C> fooClazz) throws UnsupportedDataTypeException {
if (Enum.class.isAssignableFrom(fooClazz))
return (Bla<C>) new Bla.EnumBla<E>((Class<E>) fooClazz);
if (fooClazz == Boolean.class || fooClazz == boolean.class)
return (Bla<C>) new Bla.BooleanBla();
if (Supportable.class.isAssignableFrom(idClazz))
return (Bla<C>) new Bla.SupportableBla<S>((Class<S>) fooClazz);
throw new UnsupportedDataTypeException(
"[" + fooClazz+ "] is not a supported Bla foo\n\t\t" +
"supported types are " +
"[" + Enum.class + "] " +
"[" + Boolean.class + "] " +
"[" + Supportable.class + "]");
}
我正在写一个泛型 class Bla
类型参数 T
.
我可以限制 T
,以便只能使用我想支持的 class 吗?
public class Bla<T> {
private T foo;
private Class<T> fooClazz;
}
我希望 Bla 支持大多数 原始类型 classes (Enum, Boolean, Integer, String, ...),还有我自己的接口 Supportable
.
public interface Supportable {
void doSpecific(Bla _bla);
}
Bla
有一个方法 do()
,它处理支持的 classes,或者如果使用了我不支持的 class 则抛出异常。
public void do() {
if (Enum.class.isAssignableFrom(fooClazz)) {
// Do Enum specific code
} else if (Boolean.class.isAssignableFrom(fooClazz)) {
// Do Boolean specific code
} else if (Integer.class.isAssignableFrom(fooClazz)) {
// Do Integer specific code
} else if (String.class.isAssignableFrom(fooClazz)) {
// Do String specific code
} else if (Supportable.class.isAssignableFrom(fooClazz)) {
((Supportable) foo).doSpecific();
} else {
throw new UnsupportedDataTypeException(fooClazz + "is not supported");
}
}
我知道我能做到。
public class Bla<T extends Number> {}
所以只能使用扩展Number
的classes,但是有这样的东西吗?
public class Bla<T extends Number | String> {}
那么 String
也是可能的吗?
我能想到的唯一解决方案是为不同的类型制作多个 Bla
classes。
public class BlaEnum {}
public class BlaBoolean {}
public class BlaInteger {}
public class BlaString {}
public class BlaSupportable {}
您想要支持的 classes 的通用超类型 — 考虑到您的 Supportable 类型 — 是 Object
但是,如果您将 class 定义为从 Object
(默认情况下)继承并实现 Supportable
,您无法通过这种方式限制通用 class。
如果我是你,我会使用工厂模式并编写一个接受任何事物(对象的子class)及其类型的方法来实例化 Bla
的相关实例。 Create 方法应该是通用的,但您不能在此处施加任何限制,只有当类型不是 acceptable 时您才可以抛出异常。我知道,这不是您可能期望的那种答案。但是没有其他办法(在你的情况下)。
P.S 对于那些认为OP做错事的人,我们不应该在现实世界中有这样的设计。我想介绍一个例子。假设您要编写一个 class,它假设创建一个 in-memory table(就像我们在数据库中所做的那样,但使用 java 数据类型)。您还想支持用户的数据类型!那你会怎么做呢?
限制它的一种方法是使用静态重载工厂方法来构造对象。
public class Bla<T> {
private final T foo;
private final Class<T> fooClazz;
private Bla(T foo, Class<T> fooClazz) { // Must be private
this.foo = foo;
this.fooClazz = fooClazz;
}
@SuppressWarnings("unchecked")
public static <E extends Enum<E>> Bla<E> of(E foo) { // Caveat: Cannot handle null
return new Bla<>(foo, (Class<E>) foo.getClass());
}
public static Bla<Boolean> of(Boolean foo) {
return new Bla<>(foo, Boolean.class);
}
public static Bla<Integer> of(Integer foo) {
return new Bla<>(foo, Integer.class);
}
public static Bla<String> of(String foo) {
return new Bla<>(foo, String.class);
}
public static Bla<Supportable> of(Supportable foo) {
return new Bla<>(foo, Supportable.class);
}
public void do() {
// ...
}
// ...
}
它改变了调用者构造实例的方式,但实际上也简化了它,因为调用者不必传入 Class<T>
,例如
// With constructor (old way)
Bla<MyEnum> e2 = new Bla<>(MyEnum.A, MyEnum.class);
Bla<Boolean> b2 = new Bla<>(true, Boolean.class);
Bla<Integer> i2 = new Bla<>(42, Integer.class);
Bla<String> s2 = new Bla<>("", String.class);
Bla<Supportable> su2 = new Bla<>(supportable, Supportable.class);
// With static factory method (new way)
Bla<MyEnum> e1 = Bla.of(MyEnum.A);
Bla<Boolean> b1 = Bla.of(true);
Bla<Integer> i1 = Bla.of(42);
Bla<String> s1 = Bla.of("");
Bla<Supportable> su1 = Bla.of(supportable);
// Unsupported types are not allowed
Bla<Double> i1 = Bla.of(3.14); // Error: The method of(E) in the type Bla is not applicable for the arguments (double)
但是,在 do()
方法中不应使用 multi-way if
语句,而应使用 sub类。 sub类 对调用者是隐藏的,所以它没有外部区别,但它消除了对 multi-way if
语句/switch
语句的需要:
public abstract class Bla<T> {
private final T foo;
private final Class<T> fooClazz;
private Bla(T foo, Class<T> fooClazz) { // Must be private
this.foo = foo;
this.fooClazz = fooClazz;
}
@SuppressWarnings("unchecked")
public static <E extends Enum<E>> Bla<E> of(E foo) { // Caveat: Cannot handle null
return new Bla<>(foo, (Class<E>) foo.getClass()) {
@Override
public void do() {
// Do Enum specific code
}
};
}
public static Bla<Boolean> of(Boolean foo) {
return new Bla<>(foo, Boolean.class) {
@Override
public void do() {
// Do Boolean specific code
}
};
}
public static Bla<Integer> of(Integer foo) {
return new Bla<>(foo, Integer.class) {
@Override
public void do() {
// Do Integer specific code
}
};
}
public static Bla<String> of(String foo) {
return new Bla<>(foo, String.class) {
@Override
public void do() {
// Do String specific code
}
};
}
public static Bla<Supportable> of(Supportable foo) {
return new Bla<>(foo, Supportable.class) {
@Override
public void do() {
foo.doSpecific(this);
}
};
}
public abstract void do(); // Is now abstract
// ...
}
您当然可以创建(私有)静态嵌套 类,或 (package-private) top-level 类,而不是匿名的 类,如果你愿意。
使用 sub类 允许在多个方法中执行 fooClass
特定操作。如果你只有一个方法,你可以使用 lambdas 表达式 and/or 方法引用代替:
public class Bla<T> {
private final T foo;
private final Class<T> fooClazz;
private final Consumer<Bla<T>> doImpl;
private Bla(T foo, Class<T> fooClazz, Consumer<Bla<T>> doImpl) { // Must be private
this.foo = foo;
this.fooClazz = fooClazz;
this.doImpl = doImpl;
}
@SuppressWarnings("unchecked")
public static <E extends Enum<E>> Bla<E> of(E foo) { // Caveat: Cannot handle null
return new Bla<>(foo, (Class<E>) foo.getClass(), bla -> {
// Do Enum specific code
});
}
public static Bla<Boolean> of(Boolean foo) {
return new Bla<>(foo, Boolean.class, bla -> {
// Do Boolean specific code
});
}
public static Bla<Integer> of(Integer foo) {
return new Bla<>(foo, Integer.class, bla -> {
// Do Integer specific code
});
}
public static Bla<String> of(String foo) {
return new Bla<>(foo, String.class, bla -> {
// Do String specific code
});
}
public static Bla<Supportable> of(Supportable foo) {
return new Bla<>(foo, Supportable.class, foo::doSpecific);
}
public void do() {
doImpl.accept(this);
}
// ...
}
所以要恢复我的进步。
在@Iman提示使用工厂模式后,我开始使用静态重载工厂方法来实现它。
这是我使用 Enum
、Boolean
和 Supportable
接口作为示例生成的代码。它类似于@Andreas 发布的代码。
public static <E extends Enum<E>> Bla<E> createBla(E id) {
return new Bla.EnumBla<E>((Class<E>) id.getClass()).setID(id);
}
public static Bla<Boolean> createBla(boolean id) {
return new Bla.BooleanBla().setID(id);
}
public static <S extends Supportable> Bla<S> createBla(S id) {
return new Bla.SupportableBla<S>((Class<S>) id.getClass()).setID(id);
}
我决定为我想要支持的类型创建 Bla
的静态子类。
public abstract class Bla<T> {
private T foo;
private Bla() {}
public T getFoo() { return foo; }
public Bla<T> setFoo(T foo) { this.foo = foo; return this; }
public abstract void do();
public static class EnumBla<E extends Enum<E>> extends Bla<E> {
private final Class<E> fooClazz;
public EnumBla(Class<E> fooClazz) { super(); this.fooClazz = fooClazz; }
@Override
protected void do() { // Do Enum specific code}
}
public static class BooleanBla extends Bla<Boolean> {
public BooleanBla() { super(); }
@Override
protected void do() { // Do Boolean specific code }
}
public static class SupportableBla<S extends Supportable> extends Bla<S> {
private final Class<S> fooClazz;
public SupportableBla(Class<S> fooClazz) { super(); this.fooClazz = fooClazz; }
@Override
protected void do() { if(super.id != null) super.id.doSpecific(this); }
}
}
我在 BooleanBla
中没有 fooClazz
,因为那里不需要它。
此外,我无法完全删除“嵌套”if 语句,因为我想提供在没有所需 foo 类型实例的情况下创建 Bla
的能力。
public static <C, E extends Enum<E>, S extends Supportable> Bla<C> createBla(Class<C> fooClazz) throws UnsupportedDataTypeException {
if (Enum.class.isAssignableFrom(fooClazz))
return (Bla<C>) new Bla.EnumBla<E>((Class<E>) fooClazz);
if (fooClazz == Boolean.class || fooClazz == boolean.class)
return (Bla<C>) new Bla.BooleanBla();
if (Supportable.class.isAssignableFrom(idClazz))
return (Bla<C>) new Bla.SupportableBla<S>((Class<S>) fooClazz);
throw new UnsupportedDataTypeException(
"[" + fooClazz+ "] is not a supported Bla foo\n\t\t" +
"supported types are " +
"[" + Enum.class + "] " +
"[" + Boolean.class + "] " +
"[" + Supportable.class + "]");
}