确保不同类型的泛型
Ensure different types of generics
我正在尝试创建一个通用转换器接口,它将使用相同的方法名称将 T
对象转换为 U
对象,即 convert
:
public interface GenericConverter<T, U> {
T convert(U fromObject);
U convert(T fromObject);
}
当然,泛型擦除在编译期间将这两种方法转换为以下方法:
convert(object fromObject);
所以两种方式的擦除都是一样的,导致编译时出错
在我的示例中,我将始终对 T
和 U
使用不同的对象类型是合乎逻辑的。有没有办法保持相同的方法名称(转换),能够封装 T
和 U
是不同类型的事实,并且 确保将调用正确的方法每个案例?
除非 T
和 U
这两种类型基于两个不同的类型层次结构(即每个类型总是有一些不同的超类),否则无法让这两个方法具有相同的名称.在这种情况下,它甚至在语义上都没有意义 - 如果您无法在任何合理的情况下区分这两种类型,那么这两种方法之间的语义差异应该是什么?
除了建议的方法重命名之外,还可以考虑在界面中只使用一个这样的方法,而不是在需要双向转换的地方使用 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);
即使 T
和 U
相同,这种方法也有效。
希望对您有所帮助。
祝你好运
由于类型擦除,这不可能直接实现。其他答案中已经列出了几个选项。其中之一隐含地旨在 分离 转换。因此,您可以拥有两个不同的转换器,而不是只有一个转换器:
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)
);
}
我正在尝试创建一个通用转换器接口,它将使用相同的方法名称将 T
对象转换为 U
对象,即 convert
:
public interface GenericConverter<T, U> {
T convert(U fromObject);
U convert(T fromObject);
}
当然,泛型擦除在编译期间将这两种方法转换为以下方法:
convert(object fromObject);
所以两种方式的擦除都是一样的,导致编译时出错
在我的示例中,我将始终对 T
和 U
使用不同的对象类型是合乎逻辑的。有没有办法保持相同的方法名称(转换),能够封装 T
和 U
是不同类型的事实,并且 确保将调用正确的方法每个案例?
除非 T
和 U
这两种类型基于两个不同的类型层次结构(即每个类型总是有一些不同的超类),否则无法让这两个方法具有相同的名称.在这种情况下,它甚至在语义上都没有意义 - 如果您无法在任何合理的情况下区分这两种类型,那么这两种方法之间的语义差异应该是什么?
除了建议的方法重命名之外,还可以考虑在界面中只使用一个这样的方法,而不是在需要双向转换的地方使用 GenericConverter<T, U>
和 GenericConverter<U, T>
。
问题
当你说,
it is logical that I will always use different object types for
T
andU
编译器不知道。可以强制类型相同,但不能不同(没有约束)。
方法一
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);
即使 T
和 U
相同,这种方法也有效。
希望对您有所帮助。
祝你好运
由于类型擦除,这不可能直接实现。其他答案中已经列出了几个选项。其中之一隐含地旨在 分离 转换。因此,您可以拥有两个不同的转换器,而不是只有一个转换器:
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)
);
}