方法参数泛型
最近,有关Java泛型的一个非常有趣的问题发布到Stack Overflow和reddit上。 请考虑以下方法:
<X extends CharSequence> X getCharSequence() {return (X) "hello";
}
尽管这种不安全的转换看起来有些古怪,并且您可能会猜这里有些问题,但是您仍然可以继续并在Java 8中编译以下赋值:
Integer x = getCharSequence();
这显然是错误的,因为Integer
是final
,因此没有可能也可以实现CharSequence
Integer
子类型。 但是,Java的泛型类型系统并不关心类是否为final
final,因此,在将交集类型转换回Integer
之前,它会推断X
的交集类型Integer & CharSequence
。 从编译器的角度来看,一切都很好。 在运行时: ClassCastException
尽管上面的内容“显然是可疑的”,但真正的问题出在其他地方。
(几乎)永远不会使方法仅在返回类型上通用
此规则有例外。 这些异常是类似的方法:
class Collections {public static <T> List<T> emptyList() { ... }
}
此方法没有参数,但返回通用List<T>
。 无论<T>
的具体推论如何,为什么都能保证正确性? 由于其语义。 无论您要查找的是一个空的List<String>
还是一个空的List<Integer>
,由于是空的(并且是不可变的!)语义,尽管擦除,都可以为这些T中的任何一个提供相同的实现。
另一个例外是构建器,例如javax.persistence.criteria.CriteriaBuilder.Coalesce<
,它是通过通用的无参数方法创建的:
<T> Coalesce<T> coalesce();
生成器方法是最初构造空对象的方法。 空虚是关键。
但是,对于大多数其他方法,这是不正确的,包括上述的getCharSequence()
方法。 此方法唯一保证的正确返回值是null
。
<X extends CharSequence> X getCharSequence() {return null;
}
…因为在Java中, null
是可以分配(和强制转换)给任何引用类型的值。 但这不是该方法作者的意图。
考虑函数式编程
方法是函数(大部分是函数),因此,预期不会有任何副作用。 无参数函数应始终返回完全相同的返回值。 就像emptyList()
一样。
但是实际上,这些方法并不是没有参数的。 它们确实具有类型参数<T>
或<X extendds CharSequence>
。 同样,由于泛型类型擦除,此参数在Java中“并未真正计数”,因为缺乏规范化,因此无法从方法/函数内部进行自省。
因此,请记住以下几点:
(几乎)永远不会使方法仅在返回类型上通用
最重要的是,如果您的用例只是为了避免Java 5之前的版本转换,例如:
Integer integer = (Integer) getCharSequence();
是否想在您的代码中找到令人讨厌的方法?
我正在使用番石榴来扫描类路径,您可能还会使用其他东西。 此代码段将在类路径上生成所有通用的无参数方法:
import java.lang.reflect.Method;
import java.util.Comparator;
import java.util.stream.Stream;import com.google.common.reflect.ClassPath;public class Scanner {public static void main(String[] args) throws Exception {ClassPath.from(Thread.currentThread().getContextClassLoader()).getTopLevelClasses().stream().filter(info -> !info.getPackageName().startsWith("slick")&& !info.getPackageName().startsWith("scala")).flatMap(info -> {try {return Stream.of(info.load());}catch (Throwable ignore) {return Stream.empty();}}).flatMap(c -> {try {return Stream.of(c.getMethods());}catch (Throwable ignore) {return Stream.<Method> of();}}).filter(m -> m.getTypeParameters().length > 0 && m.getParameterCount() == 0).sorted(Comparator.comparing(Method::toString)).map(Method::toGenericString).forEach(System.out::println);}
}
翻译自: https://www.javacodegeeks.com/2016/04/parameterless-generic-method-antipattern.html
方法参数泛型