- 浏览: 75945 次
- 性别:
- 来自: 上海
最新评论
-
rockythd:
视界这个概念终于搞清楚了,谢谢!
Java关于Scala的“视界(view bound)”的模拟 -
regular:
写了一个更通用的方法:ObjectUtils.cast。目的是 ...
Java关于Scala的“视界(view bound)”的模拟 -
lrztiancai:
谢谢分享。正在找这个!
Parsley+SpiceLib 2.4 Developer Manual -
kraft:
第二版什么时候出啊
Programming In Scala 翻译 -
justjavac:
xpf7622 写道haixu.huang@live @前的名 ...
Programming In Scala 翻译
闲来无事,想要用目前的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); }
评论
发现我前些天用的方法有问题, 报异常了:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object;
at serializable.TryGeneric.main(TryGeneric.java:16)
实验用代码如下:
import java.util.ArrayList; import java.util.Date; import java.util.List; public class TryGeneric { @SuppressWarnings("unchecked") public static void main(String[] args) { Date now = new Date(); List list = new ArrayList(); list.add(now); Date[] results = (Date[]) convertToArray(list); } @SuppressWarnings("unchecked") private static <T> T[] convertToArray(List<T> list) { return list.toArray((T[])new Object[0] ); } }
只是一个syntax sugar,只存在于编译期,强制转型其实还是存在的“伪泛型”。
泛型的出现是为了改进使用object类型时转换类型的效率。
这么的写法不如直接用实际的类型或是Object[]和List来的好一些。
Java里泛型的出现不是为了源码“更安全”么?执行效率应该没有多少区别才对。你看对这样的一个测试代码:
import java.util.ArrayList; public class TestGeneric { public static void main(String[] args) { foo1(); foo2(); } private static void foo1() { ArrayList list = new ArrayList(); MyObject obj = new MyObject(); list.add(obj); MyObject objref = (MyObject)list.get(0); } private static void foo2() { ArrayList<MyObject> list = new ArrayList<MyObject>(); MyObject obj = new MyObject(); list.add(obj); MyObject objref = list.get(0); } } class MyObject { }
其中的foo1()和foo2()编译出来是完全一样的,都是这样:
0: new #4; //class java/util/ArrayList 3: dup 4: invokespecial #5; //Method java/util/ArrayList."<init>":()V 7: astore_0 8: new #6; //class MyObject 11: dup 12: invokespecial #7; //Method MyObject."<init>":()V 15: astore_1 16: aload_0 17: aload_1 18: invokevirtual #8; //Method java/util/ArrayList.add:(Ljava/lang/Object;)Z 21: pop 22: aload_0 23: iconst_0 24: invokevirtual #9; //Method java/util/ArrayList.get:(I)Ljava/lang/Object; 27: checkcast #6; //class MyObject 30: astore_2 31: return
注意在标号为27的那条指令,那个就对应着foo1()的强制类型转换,而foo2()使用了泛型之后隐式的包含了这个操作。
我也觉得干脆不返回数组比较好。其实很多时候数组用起来都很尴尬,与Iterable<E>也不兼容。
即 FilterImpl implements A<String>, Filter<T>, B<String>
楼主代码里写死 types[0], 会给以后重构或者其他因素的代码改动, 带来风险, 欠妥.
建议, ((Class<?>)types[0].getRawType() == Filter.class) + 遍历types
泛型的出现是为了改进使用object类型时转换类型的效率。
这么的写法不如直接用实际的类型或是Object[]和List来的好一些。
<div class="quote_div">其实直接返回List类型就可以了,没必要搞得那么复杂,你这里的类型检查用 instanceof 就好,异常不是那么用的<br />不过我觉得你的想法还是不错</div>
<p><br /><br />同意wendong007的看法。<br /><br />这里的类型不安全在于设计上的缺陷,有两种解决方法:</p>
<ul>
<li>将DataBase本身设计成泛型,这时DataBase只持有某种类型的对象,就像Collection类型</li>
<li>如果想使DataBase能够容纳任何对象,原来的方法最好加上一个Class<T>类型的参数代表要过滤的对象类型。<br /></li>
</ul>
<p>public interface DataBase <br />{ <br /> public <T> T[] select(Filter<? super T> filter, Class<T> clazz); // 可以将Filter<T>改成Filter<? super T><br /> public void insert(Object obj); // 这里没有必要使用泛型,因为T只在一个地方(参数或返回值)被使用<br /> public int update(Filter<T> filter, Class<T> clazz, Updater<T> handler); // 按照这种设计,我总觉得Updater没有必要设计成泛型<br /> public int delete(Object obj); // 同样,这里也没有必要使用泛型<br />}</p>
<p> </p>
<p>下面这种捕获异常的方式实在不敢恭维,异常不是这么用的。一个副作用是它捕获了filter的异常。</p>
<p> try { <br /> T t = (T) obj; <br /> if (filter.where(t)) c.add(t); <br /> } catch (Exception ex) { <br /> continue; <br /> }</p>
javatar 写道
把Filter的check()改成where()看起来会更符合SQL标准,呵呵
我是尽量参照楼主原来的命名
吼嘞吼嘞,已经啊补充过嘞……
的确where更符合品鉴的口味。呵呵,采纳了。
<div class="quote_div">把Filter的check()改成where()看起来会更符合SQL标准,呵呵</div>
<p> </p>
<p>我是尽量参照楼主原来的命名</p>
Class<T> getDataType()
然后通过
Array.newInstance(getDataType(), 0);
来创建一个数组
public static void main(){ }
<p> </p>
<p>
</p><pre name="code" class="java">public abstract class Filter<T> {
protected Class clazz = (Class)((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
public boolean check(Object obj){
boolean flag = false;
if(clazz.isInstance(obj) && filter(obj))
flag = true;
return flag;
}
public abstract boolean filter(Object obj);
}</pre>
<p></p>
...
那你可以试下下面这种方法
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。因此,肯定是不能解决我提到的问题的。
<div class="quote_div">
<div class="quote_title">wendong007 写道</div>
<div class="quote_div"><br />其实直接返回List类型就可以了,没必要搞得那么复杂,你这里的类型检查用 instanceof 就好,异常不是那么用的不过我觉得你的想法还是不错<br /></div>
<br />呵呵,试试看吧。<br />
<pre name="code" class="java">if (obj instanceof T) ... // 不行,因为T在运行时被erasure为Object
</pre>
</div>
<p> </p>
<p>那你可以试下下面这种方法</p>
<p>
</p><pre name="code" class="java">Class clazz = (Class)((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
clazz.isInstance(obj);</pre>
<p></p>
发表评论
-
把Spring容器中的bean绑定到通过代码创建的对象
2012-04-26 16:17 2210Spring提供了对配置中创建对象的字段实例注入。但如果是通过 ... -
动态注册消息类型及处理函数
2011-10-01 15:56 934内容略。参见代码演示。 -
代码实例
2011-02-14 17:17 956代码实例文件 -
如何在类外部调用被子类覆盖的父类方法
2011-01-20 14:46 1857题目比较绕。以下用一个简单的例子说明: public cl ... -
SWT应用的开发实例:没有使用到OSGi
2011-01-14 11:27 1476添加音效,以及中奖名单回看功能。 SWT应用一枚。具体方法见 ... -
运行期代码问题检查技术的研究
2010-11-29 13:30 1097以下用我之前代码中的一个bug作为说明,解释如何实现代码在运行 ... -
代码潜在故障的动态分析
2010-11-16 12:24 1439引子 大家都听说过FindBugs的大名。这是一款静态代码分析 ... -
健壮的、便捷的、异步的SocketChannel实现
2010-04-27 10:34 8317Socket通信比较常见的问题有如下几种: 1、设置收发超时; ... -
打算研究学习一下OSGi和Equinox
2010-02-10 11:26 1075看到一本很直接讨论这个题目的书,不过要等到3月1日才出来。 ... -
关键应用服务的集群技术模拟
2010-01-08 14:41 1034集群技术,也就是俗称的Cluster,是为了保证某种应用不间断 ... -
JarSpur 检查引用包归属的小工具
2009-12-25 17:31 1031图形化的界面,允许你导入任意多的在项目中可能需要的Jar包。 ... -
class.getResourceAsStream()与ClassLoader.getResourceAsStream()的区别
2009-11-11 17:33 1999在jar包里获得流形式的资源有两种方法,一个是Class.ge ... -
MultiKeyedMap方案的实现
2009-11-10 11:55 3687方案背景 所谓“MultiKey ... -
Java2D: 硬件加速 - 第二部分 - 缓冲策略:Buffer Strategies
2009-11-02 12:52 2874原文地址:Java2D: Hardware ... -
Java2D: 硬件加速 - 第一部分 - 非恒定图像类:Volatile Image
2009-10-30 16:19 4257原文地址:Java2D: Hareware Accelerat ... -
自建的MiniChart库,目前实现了点图、折线图、柱状图和饼图
2009-07-15 11:08 1165花了大约一个星期时间做的MiniChart库。 由于现在的免费 ... -
BM方案模式匹配的Java代码实现
2009-06-17 13:47 1517速度还算快,例子里比较的文件一共371个,3,293,472字 ... -
对于经典模式匹配算法的一些改动
2009-06-12 12:44 1450从一个很长的字符串(或者数组)中,查找某个子串(模式串)是否存 ... -
读写进程的互斥锁
2009-03-16 15:27 1546以下的代码完成了对某个资源的读写互斥锁,具体说明如下:1. 若 ...
相关推荐
18.2 泛型解决方案 323 18.2.1 对比泛型类与泛化类 325 18.2.2 泛型和约束 325 18.3 创建泛型类 325 18.3.1 二叉树理论 325 18.3.2 使用泛型构造二叉树类 328 18.4 创建泛型方法 337 18.5 可变性和泛型接口 ...
这是第一部分,需要两部分一起才能解压 目录: 第1篇 编程经验与感悟篇 第1章 编程感悟6则 感悟01 程序员的成长之路 感悟02 程序员应具备的素质 感悟03 程序员的必备技能 ...第13章 项目中经常用到的20个解决方案
1.2.4 “属性”及“解决方案资源管理器”面板 1.2.5 其他面板 1.2.6 Visual Studio 2010的新特性 1.3 创建控制台应用程序 1.4 本章小结 1.5 习题 第2章 Visual C# 2010语法基础 2.1 C#语言概述 2.2 C#基础元素 ...
· 使用线程池、任务、取消、计时器和异步I/O操作来设计响应性强、稳定性高和伸缩性大的解决方案 · 借助于异常处理来进行状态管理 · 使用CLR寄宿、AppDomain、程序集加载、反射和C#的dynamic类型来构造具有动态...
· 使用线程池、任务、取消、计时器和异步I/O操作来设计响应性强、稳定性高和伸缩性大的解决方案 · 借助于异常处理来进行状态管理 · 使用CLR寄宿、AppDomain、程序集加载、反射和C#的dynamic类型来构造具有动态...
· 使用线程池、任务、取消、计时器和异步I/O操作来设计响应性强、稳定性高和伸缩性大的解决方案 · 借助于异常处理来进行状态管理 · 使用CLR寄宿、AppDomain、程序集加载、反射和C#的dynamic类型来构造具有动态...
· 使用线程池、任务、取消、计时器和异步I/O操作来设计响应性强、稳定性高和伸缩性大的解决方案 · 借助于异常处理来进行状态管理 · 使用CLR寄宿、AppDomain、程序集加载、反射和C#的dynamic类型来构造具有动态...
每个实例都是经过笔者精心筛选的,具有很强的实用性,其中一些实例是开发人员难于寻觅的解决方案。 本书两卷共计1200个例子,包括了开发中各个方面最常用的实例,是目前市场上实例最全面的开发类图书;本书实例来源...