oxb*_*kes 101 java generics reification
这是我最近在一次采访中提出的一个问题,即候选人希望看到添加到Java语言中的问题.它通常被认为是一种痛苦,Java没有具体化的仿制药,但是,当被推动时,候选人实际上无法告诉我他在那里可能取得的那些事情.
显然因为原始类型在Java中是允许的(以及不安全的检查),所以可以颠覆泛型并最终List<Integer>得到(例如)实际包含Strings.如果类型信息具体化,这显然是不可能的; 但必须有更多!
人们可以发布他们真正想要做的事情的例子,是否有可用的具体化的泛型?我的意思是,显然你可以List在运行时得到一个类型- 但是你会用它做什么?
public <T> void foo(List<T> l) {
if (l.getGenericType() == Integer.class) {
//yeah baby! err, what now?
Run Code Online (Sandbox Code Playgroud)
编辑:对此的快速更新,因为答案似乎主要是关注需要传入Class一个参数(例如EnumSet.noneOf(TimeUnit.class)).我正在寻找更多的东西,这是不可能的.例如:
List<?> l1 = api.gimmeAList();
List<?> l2 = api.gimmeAnotherList();
if (l1.getGenericType().isAssignableFrom(l2.getGenericType())) {
l1.addAll(l2); //why on earth would I be doing this anyway?
Run Code Online (Sandbox Code Playgroud)
RHS*_*ger 99
最常引诱我的是无法利用多个泛型类型的多个调度.以下是不可能的,并且在许多情况下它将是最佳解决方案:
public void my_method(List<String> input) { ... }
public void my_method(List<Integer> input) { ... }
Run Code Online (Sandbox Code Playgroud)
Bal*_*usC 81
从我遇到这个"需要"的几次,它最终归结为这个结构:
public class Foo<T> {
private T t;
public Foo() {
this.t = new T(); // Help?
}
}
Run Code Online (Sandbox Code Playgroud)
这在C#中有效,假设它T有一个默认构造函数.您甚至可以通过获取运行时类型typeof(T)并获取构造函数Type.GetConstructor().
常见的Java解决方案是传递Class<T>as参数.
public class Foo<T> {
private T t;
public Foo(Class<T> cls) throws Exception {
this.t = cls.newInstance();
}
}
Run Code Online (Sandbox Code Playgroud)
(它不一定需要作为构造函数参数传递,因为方法参数也很好,上面只是一个例子,try-catch为简洁起见也省略了)
对于所有其他泛型类型构造,可以通过一些反射帮助轻松解析实际类型.以下问答说明了用例和可能性:
gus*_*afc 34
想到类型安全.如果没有具体的泛型,向下转换为参数化类型将始终是不安全的:
List<String> myFriends = new ArrayList();
myFriends.add("Alice");
getSession().put("friends", myFriends);
// later, elsewhere
List<Friend> myFriends = (List<Friend>) getSession().get("friends");
myFriends.add(new Friend("Bob")); // works like a charm!
// and so...
List<String> myFriends = (List<String>) getSession().get("friends");
for (String friend : myFriends) print(friend); // ClassCastException, wtf!?
Run Code Online (Sandbox Code Playgroud)
此外,抽象会泄漏更少 - 至少是那些可能对其类型参数的运行时信息感兴趣的.今天,如果您需要有关其中一个通用参数类型的任何类型的运行时信息,您也必须传递它Class.这样,您的外部接口取决于您的实现(无论您是否使用RTTI关于您的参数).
Tur*_*nor 26
您可以在代码中创建通用数组.
public <T> static void DoStuff() {
T[] myArray = new T[42]; // No can do
}
Run Code Online (Sandbox Code Playgroud)
use*_*301 16
这是一个老问题,有很多答案,但我认为现有的答案是不合适的.
"具体化"只是意味着真实,通常只是意味着与类型擦除相反.
与Java Generics相关的一个大问题:
void method(List<A> l)和method(List<B> l).这是由于类型擦除,但非常小.Cog*_*man 12
具体化后,序列化将更加直接.我们想要的是
deserialize(thingy, List<Integer>.class);
Run Code Online (Sandbox Code Playgroud)
我们要做的是
deserialize(thing, new TypeReference<List<Integer>>(){});
Run Code Online (Sandbox Code Playgroud)
看起来很丑,很有趣.
在某些情况下,说出类似的话会非常有帮助
public <T> void doThings(List<T> thingy) {
if (T instanceof Q)
doCrazyness();
}
Run Code Online (Sandbox Code Playgroud)
这些东西不经常咬人,但是当它们发生时它们会咬人.
sat*_*esh 11
我对Java Geneircs的接触是非常有限的,除了其他答案已经提到的要点之外,还有一个由Maurice Naftalin和Philip Walder 撰写的Java Generics and Collections一书中解释的场景,其中有效的泛型.
由于类型不可恢复,因此不可能具有参数化异常.
例如,以下表格的声明无效.
class ParametericException<T> extends Exception // compile error
Run Code Online (Sandbox Code Playgroud)
这是因为catch子句检查抛出的异常是否与给定类型匹配.此检查与实例测试执行的检查相同,并且由于该类型不可恢复,因此上述形式的语句无效.
如果上面的代码有效,则可以采用以下方式进行异常处理:
try {
throw new ParametericException<Integer>(42);
} catch (ParametericException<Integer> e) { // compile error
...
}
Run Code Online (Sandbox Code Playgroud)
本书还提到如果定义Java泛型类似于定义C++模板(扩展)的方式,它可能会导致更高效的实现,因为这提供了更多的优化机会.但是除此之外不提供任何解释,因此知识渊博的人的任何解释(指针)都会有所帮助.
如果它们被神化,阵列可能会更好地使用泛型.
我有一个包装器,它将jdbc结果集作为迭代器提供,(这意味着我可以通过依赖注入更轻松地对数据库创建的操作进行单元测试).
API看起来像Iterator<T>T是某种类型,只能在构造函数中使用字符串构造.然后,Iterator查看从sql查询返回的字符串,然后尝试将其与类型为T的构造函数进行匹配.
在当前实现泛型的方式中,我还必须传入我将从结果集创建的对象的类.如果我理解正确,如果泛型被定义,我可以调用T.getClass()获取它的构造函数,然后不必抛出Class.newInstance()的结果,这将更加整洁.
基本上,我认为这使得编写API(而不仅仅是编写应用程序)更容易,因为你可以从对象中推断出更多,因此需要更少的配置......我不理解注释的含义,直到我看到它们被用于像spring或xstream这样的东西而不是配置的大量.
一个好处是避免原始(值)类型的装箱.这与其他人提出的阵列投诉有些相关,而在内存使用受到限制的情况下,它实际上可能会产生显着差异.
在编写能够反映参数化类型的框架时,还存在几种类型的问题.当然,这可以通过在运行时传递类对象来解决,但这会掩盖API并给框架的用户带来额外的负担.
| 归档时间: |
|
| 查看次数: |
11020 次 |
| 最近记录: |