锁定老帖子 主题:Object数组到泛型数组转换的伪解决方案
精华帖 (11) :: 良好帖 (0) :: 新手帖 (2) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-01-23
最后修改:2009-02-01
闲来无事,想要用目前的Java技术模拟一个对象数据库。最初只是停留在很简单的构想,单用户的,本地的,仅仅是为了实现一些纯对象化的数据库操作:
public interface DataBase { public <T> T[] select(Filter<T> filter); public <T> void insert(T t); public int update(Filter<T> filter, Updater<T> handler); public <T> int delete(T t); }
其中,Filter和Updater分别是两个接口,处理查询子句和更新子句。
public interface Filter<T> { public boolean where(T obj); } public interface Updater<T> { public void set(T obj); }
这么设计的目的是以一种对象化的方式处理数据,把数据库的操作和业务流程无缝的结合在一起。我说过,这只是我的一个练习,因此请仅把它看成是练习就好。数据库的对象容器用了一个泛型的List,里面可以包含任何对象。
然而在select方法的时候,碰到了一些问题。因为它要返回的是T[]类型结果,而由于T是泛型,在List 的toArray时,仅能生成Object[]类型的数组,与需求有差异。
//... @SuppressWarnings("unchecked") public <T> T[] select(Filter<T> filter) { Collection<T> c= new ArrayList<T>(); for (Object obj : pool) { try { T t = (T) obj; if (filter.where(t)) c.add(t); } catch (Exception ex) { continue; } } // 到现在都OK return c.toArray(); // 不OK,返回类型为Object[],与结果不符 return (T[]) c.toArray(); // 不OK,仍返回 Object[],ClassCastException return c.toArray(new T[0]); // 不OK,编译错误。 } //...
首先要解释一下为什么return c.toArray(new T[0]); 不能通过编译。因为据Java官方所说,为了向旧版本兼容,Java的泛型只是存在于编译期,其目的是编译期的源代码正确性校验,变成了.class之后所有泛型内容全部被抛弃。所以,new T[0]这种写法在运行期是不可能有意义的,因为没有T的具体定义。
加@SuppressWarnings("unchecked")的原因是T t = (T) obj;语句。没有想到更好的办法做泛型的类型比较,用这种方法最直接。不过可能会产生ClassCastException,所以一旦发生转换错误,只是简单地继续处理下一个元素。若有其它解决方案也请能不吝赐教,这里谢过。
现在,回到问题上来:怎么实现这个泛型的数组转换呢?
我这里有一个伪解决方案。就是从结果集合中取一个值出来,然后创建这个值类型的数组,见下:
@SuppressWarnings("unchecked") public <T> T[] select(Filter<T> filter) { Collection<T> c; //... Iterator<T> it = c.iterator(); if (it.hasNext()) { T[] tt = (T[]) Array.newInstance(it.next().getClass(), c.size()); return c.toArray(tt); } else return null; }
说这是个伪解决方案的原因是集合中的值并不就等于是T,有可能是T的子类。若集合中所有元素都是一个类别倒还好说,怕只怕有些元素又是子类的子类(孙类)。那么假设碰巧集合中第一个拿到的就是孙类,那么tt实际是孙类的数组,这样对于包含子类对象的集合执行toArray操作的时候就会出现ArrayStoreException。
好了,在暂时不考虑这个“伪”字的情况下,这里举个使用的例子:
public static void main(String[] args) { DataBase db = new DataBaseImpl(); db.insert("Hello, "); db.insert(" this "); db.insert(new Integer(3)); db.insert("world!"); String[] ss = (db.select(new Filter<String>() { @Override public boolean where(String obj) { return (obj.trim().length() > 4); } })); for (String s : ss) System.out.println(s); }
======== 补充========
春节休假在家,偶尔看了一下论坛。结合下面的评论并思考了一下,目前采用通过传入的Filter获得泛型类的方法:
import java.lang.reflect.*; import java.util.*; public class Container { private Collection<Object> container = new ArrayList<Object>(); public void add(Object obj) { container.add(obj); } @SuppressWarnings("unchecked") public <T> T[] select(Filter<T> filter) { Class clazz; Type[] types = filter.getClass().getGenericInterfaces(); if (types != null && types.length > 0) { clazz = ((Class) ((ParameterizedType) types[0]) .getActualTypeArguments()[0]); } else return null; Collection<T> c = new ArrayList<T>(); for (Object o : container) { if (clazz.isInstance(o) && filter.where((T) o)) c.add((T) o); } T[] tt = (T[]) Array.newInstance(clazz, c.size()); return c.toArray(tt); } public static void main(String[] args) { Container c = new Container(); c.add("Hello"); c.add("world"); c.add("I"); c.add("love"); c.add("you"); c.add(new Integer(4)); c.add("ever"); String[] list = c.select(new Filter<String>() { public boolean where(String s) { return (s.length() > 3); } }); for (String s : list) System.out.println(s); } } interface Filter<T> { boolean where(T t); }
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-01-23
好贴,作为用来理解范型相当不错
|
|
返回顶楼 | |
发表时间:2009-01-23
不过有个问题啊,db.insert(new Integer(3)); Integer没有length方法就会抛出异常,除非在用db的时候强制指定范型
DataBase<String> db = new DataBaseImpl<String>(); 否则check里的方法很可能执行不了啊 |
|
返回顶楼 | |
发表时间:2009-01-23
unsid 写道 不过有个问题啊,db.insert(new Integer(3)); Integer没有length方法就会抛出异常,除非在用db的时候强制指定范型 DataBase<String> db = new DataBaseImpl<String>(); 否则check里的方法很可能执行不了啊 是这么回事,因为最初的设定就是DataBase能够容纳任何种类的对象。所以原则上,所有的对象都应该能被装在DataBase的实例对象内。等到了要select的时候,因为Filter也是泛型的,所以待检测的对象必然是要符合这个的才能被拣出来。 所以你可以看到在select函数中首先做了一次强制转换。转换失败的不会被拣出来,从catch(Exception) { continue; }走了。 |
|
返回顶楼 | |
发表时间:2009-01-24
怎么有脱裤子放P感觉。。。素质素质。。
|
|
返回顶楼 | |
发表时间:2009-01-24
其实直接返回List类型就可以了,没必要搞得那么复杂,你这里的类型检查用 instanceof 就好,异常不是那么用的
不过我觉得你的想法还是不错 |
|
返回顶楼 | |
发表时间:2009-01-24
wendong007 写道 其实直接返回List类型就可以了,没必要搞得那么复杂,你这里的类型检查用 instanceof 就好,异常不是那么用的不过我觉得你的想法还是不错 呵呵,试试看吧。 if (obj instanceof T) ... // 不行,因为T在运行时被erasure为Object |
|
返回顶楼 | |
发表时间:2009-01-24
布衣大盗 写道 怎么有脱裤子放P感觉。。。素质素质。。 够粗的。 |
|
返回顶楼 | |
发表时间:2009-01-24
regular 写道
wendong007 写道
其实直接返回List类型就可以了,没必要搞得那么复杂,你这里的类型检查用 instanceof 就好,异常不是那么用的不过我觉得你的想法还是不错 呵呵,试试看吧。 if (obj instanceof T) ... // 不行,因为T在运行时被erasure为Object
那你可以试下下面这种方法
Class clazz = (Class)((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; clazz.isInstance(obj); |
|
返回顶楼 | |
发表时间:2009-01-31
wendong007 写道 ... 那你可以试下下面这种方法 Java代码 Class clazz = (Class)((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; clazz.isInstance(obj); Class clazz = (Class)((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; clazz.isInstance(obj); 这种做法是不合适的,这么做是把当前对象的泛型取出来,但当前对象是容器,不是容器内的对象,更不是T。可以注意到,这段代码里面一丝一毫都没提到过T。因此,肯定是不能解决我提到的问题的。 |
|
返回顶楼 | |