java 泛型 .net_Java泛型学习笔记--Java泛型和C#泛型比较学习(一)

总结Java的泛型前,先简单的介绍下C#的泛型,通过对比,比较学习Java泛型的目的和设计意图。C#泛型是C#语言2.0和通用语言运行时(CLR)同时支持的一个特性(这一点是导致C#泛型和Java泛型区别的最大原因,后面会介绍)。C#泛型在.NET CLR支持为.NET框架引入参数化变量支持。C#泛型更类似C++模板,可以理解,C#泛型实际上可以理解为类的模板类。我们通过代码实例来看C# 2.0泛型解决的问题,首先,我们通过一个没有泛型的迭代器的代码示例说起,代码实现如下:

interfaceIEnumerable

{voidAdd(Object x);

IEnumerator GetEnumerator();

}interfaceIEnumerator

{

Object Next();boolHasNext();

}classNoSuchElementException : ArgumentException { }classLinkedList : IEnumerable

{private classNode

{public Object Data { get; set; }public Node Next { get; set; }publicNode(Object data)

{this.Data =data;

}

}classDataEnumerator : IEnumerator

{publicDataEnumerator(Node ptr)

{this.head =ptr;

}privateNode head;public objectNext()

{if (head != null)

{

Object data=head.Data;

head=head.Next;returndata;

}else{throw newNoSuchElementException();

}

}public boolHasNext()

{return head != null;

}

}private Node head = null, tail = null;public voidAdd(Object data)

{if (head == null)

{

head= newNode(data);

tail=head;

}else{

tail.Next= newNode(data);

tail=tail.Next;

}

}publicIEnumerator GetEnumerator()

{return newDataEnumerator(head);

}

}

通过以上C#代码对迭代器的实现,我们发现代码至少存在以下几个问题:

如果我不想在对LinkedList集合对象存储的时候发生装箱拆箱的性能损失,只是对特点类型的元素集合进行操作。如:我们LinkedList存储了100w的整数,用LinkedList存储的时候,每次都把整数 转出 Object,相当于在堆内存中对数字进行了100w次打包,其中的性能损失我们不能不考虑。我们考虑性能的话,我们可以修改LinkedList的实现,将内嵌类Node的Data数据类型改为int类型,修改后的实现如下:

interfaceIEnumerable

{

void Add(intx);

IEnumerator GetEnumerator();

}

interfaceIEnumerator

{

intNext();

boolHasNext();

}

classNoSuchElementException : ArgumentException { }

classLinkedList : IEnumerable

{

private classNode

{

public int Data { get; set; }

public Node Next { get; set; }

public Node(intdata)

{

this.Data =data;

}

}

classDataEnumerator : IEnumerator

{

publicDataEnumerator(Node ptr)

{

this.head =ptr;

}

privateNode head;

public intNext()

{

if (head != null)

{

int data =head.Data;

head =head.Next;

returndata;

}

else{

throw newNoSuchElementException();

}

}

public boolHasNext()

{

return head != null;

}

}

private Node head = null, tail = null;

public void Add(intdata)

{

if (head == null)

{

head = newNode(data);

tail =head;

}

else{

tail.Next = newNode(data);

tail =tail.Next;

}

}

publicIEnumerator GetEnumerator()

{

return newDataEnumerator(head);

}

}

用LinkedList存储整数虽然没有问题了,但是下一次我们需要用LinkedList存储100w的字符的话,我们还要修改LinkedList的实现。分析我们的代码实现我们会发现,当我们存储不同的数据结构的时候,我们大部分代码都是相同的,只是LinkedList存储的数据类型不一样。

LinkedList linkedList = new LinkedList(),linkedList集合存储的都是Object类型的元素,对集合进行操作的时候,如果对集合存储的代码调用时元素类型信息无法直接确定,无法判断集合存储的具体类型。例如,我们有如下代码的调用:

classProgram

{static void Main(string[] args)

{

LinkedList list= newLinkedList();

list.Add(newA());

list.Add(newB());

IEnumerator e=list.GetEnumerator();while(e.HasNext())

{

B b= e.Next() as B; //此处类型转换,当转换不成功时候,捕获异常,返回类型为空

if(b != null)

b.MethodB();

}

}

}classA

{public voidMethodA()

{

Console.WriteLine("Invoke MethodA");

}

}classB:A

{public voidMethodB()

{

Console.WriteLine("Invoke MethodA");

}

}

以上代码在调用 B b = e.Next() as B 语句的时候,此处类型转换,当转换不成功时候,捕获异常,返回类型为空。

LinkedList集合对象在存储元素的时候,对元素进行隐式类型转换(子类转换成Object类型)或者(值类型发生装箱操作)。

下面我们通过C#2.0开始支持的泛型来解决了以上的问题,泛型化修改后的代码如下:

interface IEnumerable{voidAdd(T x);

IEnumeratorGetEnumerator();

}interface IEnumerator{

T Next();boolHasNext();

}class NoSuchElementException: ArgumentException { }class LinkedList : IEnumerable{private classNode

{public T Data { get; private set; }public Node Next { get; set; }publicNode(T data)

{this.Data =data;

}

}class DataEnumerator : IEnumerator{publicDataEnumerator(Node ptr)

{this.head =ptr;

}privateNode head;publicT Next()

{if (head != null)

{

T data=head.Data;

head=head.Next;returndata;

}else{throw new NoSuchElementException();

}

}public boolHasNext()

{return head != null;

}

}private Node head = null, tail = null;public voidAdd(T data)

{if (head == null)

{

head= newNode(data);

tail=head;

}else{

tail.Next= newNode(data);

tail=tail.Next;

}

}public IEnumeratorGetEnumerator()

{return newDataEnumerator(head);

}

}

通过以上对C#泛型分析,简单总结下C#引入,在我看来至少解决了三个问题:

封装和复用通用代码逻辑;

增强了编译期类型检查,减少了运行时发生InvalidCastException异常的几率;

解决集合操作时候的装箱和拆箱的效率问题。

简答介绍了C#泛型引入解决的问题后,下面我们开始介绍今天的主角—Java泛型。我们会对比以上三个问题来对比分析Java引入泛型所解决的问题。下面同样来一段Java实现的迭代器的实现的代码,如下:

interfaceCollection {public voidadd (Object x);publicIterator iterator ();

}interfaceIterator {publicObject next ();public booleanhasNext ();

}class NoSuchElementException extendsRuntimeException {}class LinkedList implementsCollection {protected classNode {

Object elt;

Node next= null;

Node (Object elt) {this.elt =elt; }

}protected Node head = null, tail = null;publicLinkedList () {}public voidadd (Object elt) {if (head == null) { head = new Node(elt); tail =head; }else { tail.next = new Node(elt); tail =tail.next; }

}publicIterator iterator () {return newIterator () {protected Node ptr =head;public boolean hasNext () { return ptr != null; }publicObject next () {if (ptr != null) {

Object elt= ptr.elt; ptr = ptr.next; returnelt;

}else throw newNoSuchElementException ();

}

};

}

}

对比C#和Java的迭代器实现,不得不说Java的迭代器实现看起来更优雅。Java对内部类的支持,让Java在类型实现上更灵活多样。下面我们来看看Java泛型迭代器的实现如下:

interface Collection{public voidadd(A x);public Iteratoriterator();

}interface Iterator{publicA next();public booleanhasNext();

}class NoSuchElementException extendsRuntimeException {}class LinkedList implements Collection{protected classNode {

A elt;

Node next= null;

Node (A elt) {this.elt =elt; }

}protected Node head = null, tail = null;publicLinkedList () {}public voidadd (A elt) {if (head == null) { head = new Node(elt); tail =head; }else { tail.next = new Node(elt); tail =tail.next; }

}public Iteratoriterator () {return new Iterator() {protected Node ptr =head;public boolean hasNext () { return ptr != null; }publicA next () {if (ptr != null) {

A elt= ptr.elt; ptr = ptr.next; returnelt;

}else throw newNoSuchElementException ();

}

};

}

}

通过以上的代码的实现,我们可能觉得Java泛型和C#都能很好解决以上三个问题。看过下面的一段代码之后,可能你对Java泛型的会有不同的认识,代码如下:

public classProgram {public static voidmain(String[] args) {

Class> c1 = new ArrayList().getClass();

Class> c2 = new ArrayList().getClass();if(c1 ==c2)

{

System.out.println("类型相同");

}

}

}//类型相同

按C#对泛型的理解,泛型实质就是类的模板。我们认为很容易知道类ArrayList和ArrayList应该是不同的类型。但是上面的代码输出结果,Java运行结果判断ArrayList和ArrayList是相同的类型。

我们马上会有疑问,在运行状态,我们的泛型参数类型信息哪去了?我们的泛型怎么了?对java泛型有一定了解的同学马上就知道,在运行状态,我们无法获取泛型的参数信息。Java的泛型不是真正的泛型,只是编译器的泛型,不是运行时的泛型。关于为什么会这样设计,我们就需要去追溯java的历史。

Java泛型历史

Java泛型是Java 1.5开始加入的语言特性,在Java 1.5之前很长一段时间,Java程序已经在我们身边的个人设备中运行。为了支持泛型出现以前的运行在Java开发的软件中的各种库,Java语言的设计师采取一种折中的方案,Java泛型不仅能向后兼容,而且保证现有的代码和类文件也合法,仍然保持原来的意思。

Java泛型实现机制

Java泛型为了向前兼容,采取运行期类型擦出泛型参数的方式来实现。这就意味着,你在使用泛型的时候,任何具体的类型都已经被擦除。因此,以上的ArrayList和ArrayList实际上都恢复到他们的原生类型List,是同一种类型。正确理解Java泛型的类型擦除,能帮我们理解Java泛型中的很多奇怪特性。今天关于泛型就先写到这里,下一节开始介绍Java泛型的类型的擦除机制引起的很多奇怪泛型特性、Java泛型设计做出的一些弥补。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值