当前位置: 首页 > 科技观察

Java-Scala泛型快速入门教程

时间:2023-03-12 13:59:48 科技观察

泛型(Generics)是强类型编程语言中经常使用的一种技术。泛型在很多框架的代码中被广泛使用,比如我们在Java中经常看到的:ListstrList=newArrayList();ListdoubleList=newLinkedList();这里的代码部分,ArrayList是一个泛型类,List是一个泛型接口类。它们为开发者提供了不同类型的集合容器。我们可以在这个集合容器中添加String、Double等各种数据类型。不管内部存储是什么类型,集合容器提供给开发者的功能都是一样的,比如add、get等。有了泛型,我们就不需要创建StringArrayList、DoubleArrayList等集合,否则的量代码量太大,维护成本极高。在Java中,一般有三种使用泛型的方式:泛型类、泛型方法和泛型接口类。一般用尖括号<>来接收泛型参数。Java泛型类如果我们定义一个支持泛型的MyArrayList,这个列表类可以简单的支持初始化和数据写入。只要在类名后面加上,类就可以支持泛型,类内部的一些属性和方法可以使用泛型类型T。当然我们也可以给这个类加上多个泛型参数,比如publicclassMyArrayList{privateintsize;T[]elements;publicMyArrayList(intcapacity){this.size=capacity;this.elements=(T[])newObject[容量];}publicvoidset(Telement,intposition){elements[position]=element;}@OverridepublicStringtoString(){Stringresult="";for(inti=0;istrList=newMyArrayList(2);strList.set("first",0);strList.set("second",1);System.out.println(strList.toString());}}我们也可以从父类继承和扩展泛型。比如Flink源码中有这样一个类定义,子类继承父类的T,同时增加GenericKEY:publicclassKeyedStreamextendsDataStream{...}Java泛型接口类Java泛型接口类的定义与Java泛型类的定义基本相同。下面的代码展示了List接口中定义的subList方法,它截取了原列表的一部分。publicinterfaceList{...publicListsubList(intfromIndex,inttoIndex);}继承并实现该接口类的代码如下:publicclassArrayListimplementsList{...publicListsubList(intfromIndex,inttoIndex){subListRangeCheck(fromIndex,toIndex,size);returnnewSubList(this,0,fromIndex,toIndex);}}Java泛型方法泛型方法可以存在于泛型类(包括接口类)中,也可以存在于普通类中。publicclassMyArrayList{...//public关键字和返回值E之间的表示这是一个泛型方法//泛型方法中的类型E和泛型类中的类型T可以不同publicEprocessElement(Eelement){...returnE;}}从上面的代码示例可以看出,public或private关键字与方法返回值之间的尖括号表明这是一个泛型方法。泛型方法的类型E和泛型类中的T可以不同,或者如果泛型方法是泛型类的成员,泛型方法可以继续使用类中的T,或者自己定义一个新的TypeE.通配符除了用来表示泛型外,还有这种形式。称为通配符,用于容纳各种不同的泛型。泛型总结总结了Java的泛型后发现,虽然它的语法有时会比较混乱,但其实质是接受不同的数据类型,增强代码的复用性。我们可以在一个类中使用多个泛型,每个泛型一般用一个大写字母表示。Java为此提供了一些大小写约定:T代表一般的任何类。E代表元素或异常。K代表钥匙。V代表Value,通常和K一起使用。比如Java的泛型给开发者提供了很多方便,特别是保证了底层代码的简洁性,因为这些底层代码通常被封装成一个框架,会有各种各样的上层应用调用这些低层代码进行具体的业务处理,每次调用都可能涉及泛型问题。比如大数据框架Spark和Flink都需要开发者基于泛型来处理数据。以上只是对泛型的简单介绍。其实在使用的时候还是有一些细节需要注意的。类型擦除Java的泛型有一个遗留问题,那就是类型擦除(TypeErasure)。我们先看下面的代码:ClassstrListClass=newArrayList().getClass();ClassintListClass=newArrayList().getClass();//Output:classjava.效用。ArrayListSystem.out.println(strListClass);//输出:classjava.util.ArrayListSystem.out.println(intListClass);//输出:trueSystem.out.println(strListClass.equals(intListClass));虽然我们使用String和Integer相加,但是泛型类型的信息在运行时被抹掉了,我们无法区分strListClass和intListClass这两个类型。这是因为泛型信息只存在于代码编译阶段,当程序在JVM上运行时,与泛型相关的信息将被抹去。类型擦除对于大部分应用系统开发者来说不是很相关,但是对于一些框架开发者来说,是必须要注意的。比如Spark和Flink的开发者已经使用了一些方法来解决类型擦除问题,API调用者并没有受到太大的影响。Scala中的泛型在对Java中的泛型有了基本的了解之后,让我们来看看Scala中的泛型。相比之下,Scala的类型系统要复杂得多。本文仅介绍一些简单的语法,帮助读者理解部分源码。在Scala中,泛型包含在方括号[]中。或者我们可以简单的理解为原来的Java泛型类现在可以改成[T]。我们创建了Stack[T]的通用类并实现了两个简单的方法。类中的所有成员和方法都可以使用泛型T。我们还定义了一个泛型方法,比如isStackPeekEquals[T],方法中可以使用泛型T。objectMyStackDemo{//Stack泛型类classStack[T]{privatevarelements:List[T]=Nildefpush(x:T){elements=x::elements}defpeek:T=elements.head}//泛型方法,检查两个是否两个Stack的顶部是相同的={valstack=newStack[Int]stack.push(1)stack.push(2)println(stack.peek)valstack2=newStack[Int]stack2.push(2)valstack3=newStack[Int]stack3.push(3)println(isStackPeekEquals(stack,stack2))println(isStackPeekEquals(stack,stack3))}}总结本文简单介绍了Java/Scala的泛型,它允许数据类型可变,提高了代码的复用性。很多框架都会用到技术,开发者了解泛型的基本用法是非常有必要的。