确保不同类型的泛型

Ensure different types of generics

我正在尝试创建一个通用转换器接口,它将使用相同的方法名称将 T 对象转换为 U 对象,即 convert:

public interface GenericConverter<T, U> {
  T convert(U fromObject);
  U convert(T fromObject);
}

当然,泛型擦除在编译期间将这两种方法转换为以下方法:

convert(object fromObject);

所以两种方式的擦除都是一样的,导致编译时出错

在我的示例中,我将始终对 TU 使用不同的对象类型是合乎逻辑的。有没有办法保持相同的方法名称(转换),能够封装 TU 是不同类型的事实,并且 确保将调用正确的方法每个案例?

除非 TU 这两种类型基于两个不同的类型层次结构(即每个类型总是有一些不同的超类),否则无法让这两个方法具有相同的名称.在这种情况下,它甚至在语义上都没有意义 - 如果您无法在任何合理的情况下区分这两种类型,那么这两种方法之间的语义差异应该是什么?

除了建议的方法重命名之外,还可以考虑在界面中只使用一个这样的方法,而不是在需要双向转换的地方使用 GenericConverter<T, U>GenericConverter<U, T>

问题

当你说,

it is logical that I will always use different object types for T and U

编译器不知道。可以强制类型相同,但不能不同(没有约束)。


方法一

class ConversionSource {}

class ConversionTarget {}

interface GenericConverter<T extends ConversionSource, U extends ConversionTarget> {
    T convert(U obj);
    U convert(T obj);
}

现在,擦除是不同的。您可以使用所需的源获得所需的行为,但由于限制,使用受到严格限制。


方法二

interface InvertibleConverter<T, U> {
    U convert(T obj);
    InvertibleConverter<U, T> inverse();
}

class Tokenizer implements InvertibleConverter<String, Stream<String>> {

    @Override
    Stream<String> convert(String obj) {
        return Arrays.stream(obj.split(" "));
    }

    @Override
    InvertibleConverter<Stream<String>, String> inverse() {
        return new InvertibleConverter<Stream<String>, String>() {
            @Override
            public String convert(Stream<String> obj) {
                return obj.collect(Collectors.joining(" "));
            }

            @Override
            public InvertibleConverter<String, Stream<String>> inverse() {
                return Tokenizer.this;
            }
        };
    }
}

用法可以如下

InvertibleConverter<String, Stream<String>> splitter = new Tokenizer();
String sentence = "This is a sentence";
Stream<String> words = splitter.convert(sentence);
String sameSentence = splitter.inverse().convert(words);

即使 TU 相同,这种方法也有效。

希望对您有所帮助。
祝你好运

由于类型擦除,这不可能直接实现。其他答案中已经列出了几个选项。其中之一隐含地旨在 分离 转换。因此,您可以拥有两个不同的转换器,而不是只有一个转换器:

public interface GenericConverter<T, U> {
  U convert(T fromObject);
}

GenericConverter<Integer, String> forward = Converters.integerString();
GenericConverter<String, Integer> backward = Converters.stringInteger();

但请注意,在这种情况下,GenericConverter 接口在结构上等同于 Function 接口 - 因此可能没有理由创建一个新接口。

相反,如果您想将此 "forward and backward converter" 作为某种 命名实体 (两个转换函数不可分割地链接在一起),您可以定义一个接口为此:

public interface GenericConverter<T, U> {
  Function<T, U> forward();
  Function<U, T> backward();
}

可以这样使用:

GenericConverter<Integer, String> converter = Converters.integerString();

String string = converter.forward().apply(someInteger);
Integer integer = converter.backward().apply(someString);

这里是否是 "best" 解决方案取决于预期的使用模式。一个优点可能是,使用像这样的通用 (!) 效用函数...

private static GenericConverter<T, U> create(
    Function<T, U> forward, Function<U, T> backward) {
    return new GenericConverter() {
        @Override
        public Function<T, U> forward() {
            return forward;
        }
        @Override
        public Function<U, T> backward() {
            return backward;
        }
    }
}

创建一个新的转换器非常简单:

public static GenericConverter<Integer, String> integerString() {
    return create(
        integer -> String.valueOf(integer), 
        string -> Integer.parseInt(string)
    );
}