Java:将泛型类型(T)对象转换为类型化调用签名可以识别的完全类型化对象?

pqwbnv8z  于 2023-02-28  发布在  Java
关注(0)|答案(3)|浏览(212)

给定

private someMethod(String value) {
 // do string things
}

private someMethod(Integer value) {
 // do number things
}

根据clazz的类型和input的值,下面的代码是否可以调用正确的someMethod

public <T> void doSomething(T input, Class<T> clazz) {
    // Cast the input to the type specified by clazz
    // Call a method that expects a parameter of the type specified by clazz
    someMethod(clazz.cast(input));
}

如果不能这样做,那么最好的方法是什么,而不用对静态类型常量进行长时间的if/else硬检查?

qlzsbp2j

qlzsbp2j1#

第一个(快速)“解决方案”

实际上,没有Java内置的机制来为您处理这种调度,这意味着您必须自己完成。
但是...为了避免更长的if-else链,您可以使用一个Map,将类型Map到您想要调用的消费方法。

private final Map<Class<?>, Consumer<?>> dispatcher = new HashMap<>();

{
    dispatcher.put(String.class, (String value) -> someMethod(value));
    dispatcher.put(Integer.class, (Integer value) -> someMethod(value));
}

现在您可以执行以下操作:

public <T> void doSomething(T input, Class<T> clazz) {
    Consumer<T> consumer = (Consumer<T>) dispatcher.get(clazz);
    if (consumer != null) consumer.accept(input);
}

构建调度Map时要小心,任何错误都会导致调用doSomething方法时出现运行时错误,甚至会出现编译器警告。
不是很完美,但也许是一条路要走...

进一步考虑

这个手动调度对你来说是一个很好的提示,可能会有更多像OOP的解决方案。通常,在你的思想后面潜伏着一个 * 类型 *...
因此,首先为该类型创建一个接口:

interface SomethingDoable {
    void someMethod();
}

注意,泛型类型在这里也被删除了,现在你可以为它创建一些类了:

class StringDoable implements SomethingDoable {
    private final String value;
    public StringDoable(String value) { this.value = value; }
    @Override public void doSomething() { /* your code here using the string value */ }
}

class IntegerDoable implements SomethingDoable {
    private final Integer value;
    public IntegerDoable(Integer value) { this.value = value; }
    @Override public void doSomething() { /* your code here using the integer value */ }
}

有了这些准备,您就可以简化您的公共方法:

public void doSomething(SomethingDoable somethingDoable) {
    somethingDoable.doSomething();
}

使用它就像这样简单:

doSomething(new StringDoable("42"));
doSomething(new IntegerDoable(42));

这种方法的主要优点是......扩展它仅仅意味着添加另一个具体的类。不需要更改其他任何内容。

nafvub8i

nafvub8i2#

在运行时,Java只根据调用方法的对象动态地分派方法,即从被调用者的Angular 来看this,也就是“receiver parameter”,或者你在调用中调用的这一部分:

animal.eat(food);
^^^^^^

this.someMethod(input);
^^^^

运行时选择哪种方法完全取决于该部分。如果foo在运行时存储Dog的示例,则调用Dog.eat。如果它存储Cat的示例,则调用Cat.eat
但是,这里要做的似乎是基于food在运行时存储的类型进行动态调度,因为Java没有这样做,所以要么使用if-else-if链手动调度,要么更改调用,使food成为接收方:

food.getsEatenBy(animal);

input.someMethod(this); // not sure if your someMethod needs "this" or not...

因为在您的例子中参数是StringInteger,所以您需要为它们编写 Package 器类,以便能够向它们添加方法。

// let's also introduce an interface that all the inputs of someMethod implements
public interface SomeMethodInput {
    // SomeMethodClass is the class where someMethod originally was
    void someMethod(SomeMethodClass self);
}

public class StringInput implements SomeMethodInput {

    private String value;

    public StringInput(String string) {
        this.value = string;
    }

    public String getValue() {
        return value;
    }

    @Override
    public void someMethod(SomeMethodClass self) {
        // do string things...
    }
}

public class IntegerInput implements SomeMethodInput {

    private Integer value;

    public IntegerInput(Integer integer) {
        this.value = integer;
    }

    public Integer getValue() {
        return value;
    }

    @Override
    public void someMethod(SomeMethodClass self) {
        // do integer things...
    }
}

现在doSomething只需要取一个SomeMethodInput

public void doSomething(SomeMethodInput input) {
    input.someMethod(this);
}

您可能会注意到这与visitor模式之间的一些相似之处,您是对的-这正是visitor模式实现this和参数双重调度的方式。我在这里展示的还不是这样,因为它只对参数进行调度。

ogq8wdun

ogq8wdun3#

您可以使用反射API来实现这一点。

public <T> void someMethod(Object value, Class<T> clazz) {
    try {
        getClass().getDeclaredMethod("someMethod", clazz).invoke(this, value);
    } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
        throw new RuntimeException(e);
    }
}

相关问题