您可以将类型参数<T>限制为多个特定类吗?

时间:2020-10-25 15:22:44

标签: java class generics bounded-wildcard

我正在编写类型参数为Bla的泛型类T

我可以限制T,以便仅使用我要支持的类吗?

public class Bla<T> {
    private T foo;
    private Class<T> fooClazz;
}

我希望Bla支持大多数原始类(Enum,Boolean,Integer,String等),以及我自己的接口Supportable

public interface Supportable {
    void doSpecific(Bla _bla);
}

Bla具有方法do(),该方法处理受支持的类,如果使用了我不支持的类,则抛出异常。

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的类,但是有这样的东西吗?

public class Bla<T extends Number | String> {}

那么String也是可能的吗?

我唯一想到的解决方案是为不同类型创建多个Bla类。

public class BlaEnum {}
public class BlaBoolean {}
public class BlaInteger {}
public class BlaString {}
public class BlaSupportable {}

3 个答案:

答案 0 :(得分:2)

一种限制它的方法是使用静态重载工厂方法来构造对象。

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)

但是,它应该使用子类,而不是在if方法中使用多向do()语句。子类对调用者隐藏,因此没有任何外部差异,但消除了对多向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

    // ...
}

如果愿意,您当然可以创建(私有)静态嵌套类或(程序包私有)顶级类,而不是匿名类。

使用子类可以在多种方法中进行特定于fooClass的操作。如果只有一种方法,则可以改用lambdas表达式和/或方法引用:

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);
    }

    // ...
}

答案 1 :(得分:1)

要支持的类(并考虑可支持的类型)的常见超类型为Object,如果您定义要从Object继承的类(默认情况下)并实现{ {1}}无法以这种方式限制通用类。

如果我是您,那么我将使用Factory模式并编写一个接受任何事物(Object的子类)及其类型的方法来实例化Supportable的相关实例。 Create方法应该是通用的,但是您不能在此施加任何限制,只有在类型不可接受的情况下您才可以抛出异常。我知道,这不是您可能期望的答案类型。但是没有其他方法(针对您的情况)。

P.S对于那些认为OP做错了事情的人,我们在现实世界中不应有这样的设计。我想举一个例子。假设您将要编写一个类,假设它要创建一个内存表(就像我们在数据库中所做的一样,但是使用Java数据类型)。您还希望支持用户的数据类型!那你该怎么做呢?

答案 2 :(得分:1)

所以要恢复我的进度。

在使用 Iman 提示使用工厂模式之后,我开始使用静态重载工厂方法来实现它。 这是我使用EnumBooleanSupportable接口作为示例的代码。它类似于发布的代码<< strong> 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); }
    }
}

我在fooClazz中没有BooleanBla,因为那里不需要。 同样,我无法完全删除“嵌套的” if语句,因为我想提供创建Bla的能力而无需使用所需的foo类型的实例。

    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 + "]");
    }
相关问题