java泛型实例 java泛型的特点(5篇)
在日常的学习、工作、生活中,肯定对各类范文都很熟悉吧。范文书写有哪些要求呢?我们怎样才能写好一篇范文呢?以下是我为大家搜集的优质范文,仅供参考,一起来看看吧
java泛型实例 java泛型的特点篇一
java泛型是java1.5中引入的新特性,在此之前,java通过对类型object的引用来实现参数类型的“任意化”,特点则是需要进行显示的强制类型转换。(而这种显示的`类型转换可能是无法进行的,是错误的)但编译器无法发现强制类型转换可能引起的异常,异常只有在运行时才能出现,成为了系统的安全隐患。----------------------------为啥要进入泛型这个概念 泛型的本质是参数化类型,及所操作的数据类型被指定为一个参数,此参数类型可以用在类、接口、和方法的声明及创建中,分别被称为泛型类,泛型接口,及泛型方法。 使用注意事项:
泛型的类型参数只能是类类型(包括自定义类),但是不能是简单类型 泛型类型参数可以是多个
泛型的参数类型还可以是通配符类型
没有泛型的错误:
import .*;
public class nogenerictypedemo{
public static void main(string[] args)
{
list names=new arraylist();
("张桑拿");
(new integer(2);
string namefirst=(string)(0);
string namesecond=(string)(1);//astexception异常,而且编译时没有被发现
}
有时候需要使泛型变量能使用任何的类型,此时可以使用通配符”?”否则可能需要编写许多版本的重载函数,使用通配符,使方法printlsit()可以接受各种类型的list对象,否则必须使用重载技术,
public static void printlist(listlist)
{
//输出集合中的元素
}
java泛型实例 java泛型的特点篇二
导读:本文是从《java generics quick tutorial》这篇文章翻译而来,译文来自外刊it评论《java泛型简明教程》。内容如下:
泛型是java se 5.0中引入的一项特征,自从这项语言特征出现多年来,我相信,几乎所有的java程序员不仅听说过,而且使用过它。关于java泛型的教程,免费的,不免费的,有很多。我遇到的最好的教材有:
the java tutorial
java generics and collections, by maurice naftalin and philip wadler
effective java中文版(第2版), by joshua bloch.
尽管有这么多丰富的资料,有时我感觉,有很多的程序员仍然不太明白java泛型的功能和意义。这就是为什么我想使用一种最简单的形式来总结一下程序员需要知道的关于java泛型的最基本的知识。
java泛型由来的动机
理解java泛型最简单的方法是把它看成一种便捷语法,能节省你某些java类型转换(casting)上的操作:
1. listbox = ...;
2. apple apple = (0);
上面的代码自身已表达的很清楚:box是一个装有apple对象的list。get方法返回一个apple对象实例,这个过程不需要进行类型转换。没有泛型,上面的代码需要写成这样:
1. list box = ...;
2. apple apple = (apple) (0);
很明显,泛型的主要好处就是让编译器保留参数的类型信息,执行类型检查,执行类型转换操作:编译器保证了这些类型转换的绝对无误。
相对于依赖程序员来记住对象类型、执行类型转换——这会导致程序运行时的失败,很难调试和解决,而编译器能够帮助程序员在编译时强制进行大量的类型检查,发现其中的错误。 泛型的构成
由泛型的构成引出了一个类型变量的概念。根据java语言规范,类型变量是一种没有限制的标志符,产生于以下几种情况:
泛型类声明
泛型接口声明
泛型方法声明
泛型构造器(constructor)声明
泛型类和接口
如果一个类或接口上有一个或多个类型变量,那它就是泛型。类型变量由尖括号界定,放在类或接口名的后面:
1. public interface listextends collection{
2.
3. ...
4. }
简单的说,类型变量扮演的角色就如同一个参数,它提供给编译器用来类型检查的信息。 java类库里的很多类,例如整个collection框架都做了泛型化的修改。例如,我们在上面的第一段代码里用到的list接口就是一个泛型类。在那段代码里,box是一个list对象,它是一个带有一个apple类型变量的list接口的类实现的实例。编译器使用这个类型变量参数在get方法被调用、返回一个apple对象时自动对其进行类型转换。 实际上,这新出现的泛型标记,或者说这个list接口里的get方法是这样的:
1. t get(int index);
get方法实际返回的是一个类型为t的对象,t是在list声明中的类型变量。 泛型方法和构造器(constructor)
非常的相似,如果方法和构造器上声明了一个或多个类型变量,它们也可以泛型化。
1. public statict getfirst(listlist)
这个方法将会接受一个list类型的参数,返回一个t类型的对象。
例子
你既可以使用java类库里提供的泛型类,也可以使用自己的泛型类。
类型安全的写入数据…
下面的这段代码是个例子,我们创建了一个list实例,然后装入一些数据:
1. liststr = new arraylist();
2.
3. ("hello ");
4.
5. ("world.");
如果我们试图在list装入另外一种对象,编译器就会提示错误:
1. (1); //不能编译
类型安全的读取数据…
当我们在使用list对象时,它总能保证我们得到的是一个string对象:
1. string mystring = (0);
遍历
类库中的很多类,诸如iterator,功能都有所增强,被泛型化。list接口里的 iterator()方法现在返回的是iterator,由它的t next()方法返回的对象不需要再进行类型转换,你直接得到正确的类型。
1. for (iteratoriter = or(); t();){
2.
3. string s = ();
4.
5. (s);
6.
7. }
使用foreach
“for each”语法同样受益于泛型。前面的代码可以写出这样:
1. for (string s: str){
2.
3. (s);
4.
5. }
这样既容易阅读也容易维护。
自动封装(autoboxing)和自动拆封(autounboxing)
在使用java泛型时,autoboxing/autounboxing这两个特征会被自动的用到,就像下面的这段代码:
1. listints = new arraylist();
2.
3. (0);
4.
5. (1);
6.
7.
8.
9.
sum = 0;
11.
(int i : ints){
13.
+= i;
15.
16.}
然而,你要明白的一点是,封装和解封会带来性能上的损失,所有,通用要谨慎的使用。 子类型
在java中,跟其它具有面向对象类型的语言一样,类型的层级可以被设计成这样:
在java中,类型t的子类型既可以是类型t的一个扩展,也可以是类型t的一个直接或非直接实现(如果t是一个接口的话)。因为“成为某类型的子类型”是一个具有传递性质的关系,如果类型a是b的一个子类型,b是c的子类型,那么a也是c的子类型。在上面的图中: fujiapple(富士苹果)是apple的子类型
apple是fruit(水果)的子类型
fujiapple(富士苹果)是fruit(水果)的子类型
所有java类型都是object类型的子类型。
b类型的任何一个子类型a都可以被赋给一个类型b的声明:
1. apple a = ...;
2. fruit f = a;
泛型类型的子类型
如果一个apple对象的实例可以被赋给一个fruit对象的声明,就像上面看到的,那么,list和 a list之间又是个什么关系呢?更通用些,如果类型a是类型b的子类型,那c 和 c
答案会出乎你的意料:没有任何关系。用更通俗的话,泛型类型跟其是否子类型没有任何关系。
java泛型实例 java泛型的特点篇三
泛型总结篇:
1)泛型参数只能是类类型
例如:list// 报错
list// 正确
2)泛型的类型参数可以有多个!
例如:listlist = new
arraylist();
3)泛型的参数类型可以使用extends,习惯称“有界类型”,
例如:list,person为list的上界
4)泛型可以使用通配符类型!“?” 相当于“object”类型,(注意不可逆) 例如:list //定义成这样可以添加object类型到list里面去 list
; //定义成这样不可以添加string类型到list里面去 list; // 这样就可以互等了!这样还可以设定泛型的上限
5)带泛型的类,构造方法写法不变,其后不可以加泛型!
例如:class student{
public student(){} // 构造方法这样写就错了
}
6)list不是 list
的子类!,不可以把前者看成后者的子类,所以不
可以把前者的实例赋给后者
例如: listslist = new arraylist();
(new string("abc"));
list
olist = new arraylist
();
("abc");
olist = slist; // 报错!
7)带不同泛型参数的实例可以共享类的静态方法和静态变量,所以静态方法和静态变量
申明的时候不可以使用类型行参
例如:class cup{
static t info; // 报错!
public static setinfo(t info){}; // 报错!
}
8)带不同泛型参数的类是共享一个字节码文件的!反编译过后泛型参数就被擦除了
例如:listslist = new arraylist();
(new string("aaa"));
listilist = new arraylist();
(new integer(100));
n(ss()==ss()); 结果为true;
9)当使用定义了泛型参数的接口 和 父类的时候!就不能在带参数了 例如:class sub extends father{} // 错误
10)
class t{}
public class typetest extends t{}//报错
class t{}
public class typetest extends t{} // 正确 可使用string等基本封装类型
class t{}
class student{}
public class typetest extends t{} // 这样也正确
11) 泛型方法 ,泛型方法的参数的作用域仅在本方法,要和方法带泛型区分开! staticvoid test(lists_list){} // 泛型参数要放在返回类型前
12)带有泛型的代码转换成没有泛型的代码由javac完成,虚拟机不处理这些事情!
这种技术叫做 “擦除”;
例如:class food{
t size;
public food(t size){} //注意构造方法不可以带泛型参数!
public void setsize(){ = size; }
public t getsize(){ return ;}
}
public class test{
public static void main(string arg[]){
fooda = new food(6);
ingeger as = e(); // 返回的是integer的类型 food b = a; //把a对象赋给food变量;泛型参数类型会丢失 即擦除;
number size1 = e(); // b只知道size的类型是number // integer in = e(); 这样是编译错误的!
} }
java泛型实例 java泛型的特点篇四
简单普通类
package test7;
import ist;
class student
{
int number;
string name;
string cla;
public int getnumber()
{
return number;
}
public void setnumber(int number)
{
thi = number;
}
public string getname()
{
return name;
}
public void setname(string name)
{
= name;
}
public string getcla()
{
return cla;
}
public void setcla(string cla)
{
thi = cla;
}
public student(){}
public student(int number,string name,string cla)
{
thi = number;
= name;
thi = cla;
}
}
public class test071 {
public static void main(string[] args) {
n("设计者:");
student s1 = new student(123, "张三", "11计科");
student s2 = new student(124, "李四", "11计科");
student s3 = new student(125, "王五", "11计科");
student dujingjing = new student(20110105,"杜","11计科");
arraylist al = new arraylist();
(s1);
(s2);
(s3);
(dujingjing);
for(int i = 0;i<();i++)
{
student s = (student)(i);
n("学号:" + + " 姓名:" ++ " 班级:" +);
}
}
}
简单泛型实例
java中泛型定义
java的泛型类就是一个用类型作为参数的类,即带有参数化类型的类。就像我们定义类的成员方法一样。大家很熟悉java的成员方法形式是method(string str,int i),方法中参数str、i的值是可变的。而泛型也是一样的:class泛型类类名,这里的k和v就像方法中的参数str和i,也是可变的。
class uset{
private t x;
public setx(t x){````````}
}
package test7;
泛型类:l2
class l2
{
private t obj;
public l2(t obj)
{
= obj;
}
public t getobj()
{
return ;
}
public void setobj(t obj)
{
= obj;
}
public void showobj(t obj)
{
n("数据为:" + ); }
}
主类:
public class test072 {
public static void main(string[] args) {
n("设计者:");
l2name = new l2("汽车"); j(());
l2data = new l2(12); j(());
}
}
简单集合类
java泛型实例 java泛型的特点篇五
1. 什么是泛型?
泛型(generic type 或者 generics)是对 java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样。
可以在集合框架(collection framework)中看到泛型的动机。例如,map 类允许您向一个 map 添加任意类的对象,即使最常见的情况是在给定映射(map)中保存某个特定类型(比如 string)的对象。
因为 () 被定义为返回 object,所以一般必须将 () 的结果强制类型转换为期望的类型,如下面的代码所示:
map m = new hashmap();
("key", "blarg");
string s = (string) ("key");
要让程序通过编译,必须将 get() 的结果强制类型转换为 string,并且希望结果真的是一个 string。但是有可能某人已经在该映射中保存了不是 string 的东西,这样的话,上面的代码将会抛出 classcastexception。
理想情况下,您可能会得出这样一个观点,即 m 是一个 map,它将 string 键映射到 string 值。这可以让您消除代码中的强制类型转换,同时获得一个附加的类型检查层,该检查层可以防止有人将错误类型的键或值保存在集合中。这就是泛型所做的工作。
2. 泛型的好处
java 语言中引入泛型是一个较大的功能增强。不仅语言、类型系统和编译器有了较大的变化,以支持泛型,而且类库也进行了大翻修,所以许多重要的类,比如集合框架,都已经成为泛型化的了。这带来了很多好处:
类型安全。 泛型的主要目标是提高 java 程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。没有泛型,这些假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中)。
java 程序中的一种流行技术是定义这样的集合,即它的元素或键是公共类型的,比如“string 列表”或者“string 到 string 的映射”。通过在变量声明中捕获这一附加的类型信息,泛型允许编译器实施这些附加的类型约束。类型错误现在就可以在编译时被捕获了,而不是在运行时当作 classcastexception 展示出来。将类型检查从运行时挪到编译时有助于您更容易找到错误,并可提高程序的可靠性。
消除强制类型转换。 泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。
尽管减少强制类型转换可以降低使用泛型类的代码的罗嗦程度,但是声明泛型变量会带来相应的罗嗦。比较下面两个代码例子。
该代码不使用泛型:
list li = new arraylist();
(new integer(3));
integer i = (integer) (0);
该代码使用泛型:
listli = new arraylist();
(new integer(3));
integer i = (0);
在简单的程序中使用一次泛型变量不会降低罗嗦程度。但是对于多次使用泛型变量的大型程序来说,则可以累积起来降低罗嗦程度。
潜在的性能收益。 泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。但是更多类型信息可用于编译器这一事实,为未来版本的 jvm 的优化带来可能。
由于泛型的实现方式,支持泛型(几乎)不需要 jvm 或类文件更改。所有工作都在编译器中完成,编译器生成类似于没有泛型(和强制类型转换)时所写的代码,只是更能确保类型安全而已。
3. 泛型用法的例子
泛型的许多最佳例子都来自集合框架,因为泛型让您在保存在集合中的元素上指定类型约束。考虑这个使用 map 类的例子,其中涉及一定程度的优化,即 () 返回的结果将确实是一个 string:
map m = new hashmap();
("key", "blarg");
string s = (string) ("key");
如果有人已经在映射中放置了不是 string 的其他东西,上面的代码将会抛出 classcastexception。泛型允许您表达这样的类型约束,即 m 是一个将 string 键映射到 string 值的 map。这可以消除代码中的强制类型转换,同时获得一个附加的类型检查层,这个检查层可以防止有人将错误类型的键或值保存在集合中。
下面的代码示例展示了 jdk 5.0 中集合框架中的 map 接口的定义的一部分:
public interface map{
public void put(k key, v value);
public v get(k key);
}
注意该接口的两个附加物:
类型参数 k 和 v 在类级别的规格说明,表示在声明一个 map 类型的变量时指定的类型的占位符。
在 get()、put() 和其他方法的方法签名中使用的 k 和 v。
为了赢得使用泛型的好处,必须在定义或实例化 map 类型的变量时为 k 和 v 提供具体的值。以一种相对直观的方式做这件事:
mapm = new hashmap();
("key", "blarg");
string s = ("key");
当使用 map 的泛型化版本时,您不再需要将 () 的结果强制类型转换为 string,因为编译器知道 get() 将返回一个 string。
在使用泛型的版本中并没有减少键盘录入;实际上,比使用强制类型转换的版本需要做更多键入。使用泛型只是带来了附加的类型安全。因为编译器知道关于您将放进 map 中的键和值的类型的更多信息,所以类型检查从执行时挪到了编译时,这会提高可靠性并加快开发速度。
向后兼容
在 java 语言中引入泛型的一个重要目标就是维护向后兼容。尽管 jdk 5.0 的标准类库中的许多类,比如集合框架,都已经泛型化了,但是使用集合类(比如 hashmap 和 arraylist)的现有代码将继续不加修改地在 jdk 5.0 中工作。当然,没有利用泛型的现有代码将不会赢得泛型的类型安全好处。
4. 泛型基础
4.1 类型参数
在定义泛型类或声明泛型类的变量时,使用尖括号来指定形式类型参数。形式类型参数与实际类型参数之间的关系类似于形式方法参数与实际方法参数之间的关系,只是类型参数表示类型,而不是表示值。
泛型类中的类型参数几乎可以用于任何可以使用类名的地方。例如,下面是 接口的定义的摘录:
public interface map{
public void put(k key, v value);
public v get(k key);
}
map 接口是由两个类型参数化的,这两个类型是键类型 k 和值类型 v。(不使用泛型)将会接受或返回 object 的方法现在在它们的方法签名中使用 k 或 v,指示附加的类型约束位于 map 的规格说明之下。
当声明或者实例化一个泛型的对象时,必须指定类型参数的值:
mapmap = new ha
shmap();
注意,在本例中,必须指定两次类型参数。一次是在声明变量 map 的类型时,另一次是在选择 hashmap 类的参数化以便可以实例化正确类型的一个实例时。
编译器在遇到一个 map类型的变量时,知道 k 和 v 现在被绑定为 string,因此它知道在这样的变量上调用 () 将会得到 string 类型。
除了异常类型、枚举或匿名内部类以外,任何类都可以具有类型参数。
4.2 命名类型参数
推荐的命名约定是使用大写的单个字母名称作为类型参数。这与 c++ 约定有所不同(参阅 附录 a:与 c++ 模板的比较),并反映了大多数泛型类将具有少量类型参数的假定。对于常见的泛型模式,推荐的名称是:
k —— 键,比如映射的键。
v —— 值,比如 list 和 set 的内容,或者 map 中的值。
e —— 异常类。
t —— 泛型。
4.3 泛型不是协变的
关于泛型的混淆,一个常见的来源就是假设它们像数组一样是协变的。其实它们不是协变的。list
不是 list的父类型。
如果 a 扩展 b,那么 a 的数组也是 b 的数组,并且完全可以在需要 b[] 的地方使用 a[]:
integer[] intarray = new integer[10];
number[] numberarray = intarray;
上面的代码是有效的,因为一个 integer 是一个 number,因而一个 integer 数组是 一个 number 数组。但是对于泛型来说则不然。下面的代码是无效的:
listintlist = new arraylist();
listnumberlist = intlist; // invalid
最初,大多数 java 程序员觉得这缺少协变很烦人,或者甚至是“坏的(broken)”,但是之所以这样有一个很好的原因。如果可以将 list赋给 list,下面的代码就会违背泛型应该提供的类型安全:
listintlist = new arraylist();
listnumberlist = intlist; // invalid
(new float(3.1415));
因为 intlist 和 numberlist 都是有别名的,如果允许的话,上面的代码就会让您将不是 integers 的东西放进 intlist 中。但是,正如下一屏将会看到的,您有一个更加灵活的方式来定义泛型。
4.4 类型通配符
假设您具有该方法:
void printlist(list l) {
for (object o : l)
n(o);
}
上面的代码在 jdk 5.0 上编译通过,但是如果试图用 list调用它,则会得到警告。出现警告是因为,您将泛型(list)传递给一个只承诺将它当作 list(所谓的原始类型)的方法,这将破坏使用泛型的类型安全。
如果试图编写像下面这样的方法,那么将会怎么样?
void printlist(list
l) {
for (object o : l)
n(o);
}
它仍然不会通过编译,因为一个 list不是 一个 list
(正如前一屏 泛型不是协变的 中所学的)。这才真正烦人 —— 现在您的泛型版本还没有普通的非泛型版本有用!
解决方案是使用类型通配符:
void printlist(list l) {
for (object o : l)
n(o);
}
上面代码中的问号是一个类型通配符。它读作“问号”。list 是任何泛型 list 的父类型,所以您完全可以将 list
、list或 list<list
> 传递给 printlist()。
4.5 类型通配符的作用类型通配符中引入了类型通配符,这让您可以声明 list 类型的变量。您可以对这样的 list 做什么呢?非常方便,可以从中检索元素,但是不能添加元素。原因不是编译器知道哪些方法修改列表哪些方法不修改列表,而是(大多数)变化的方法比不变化的方法需要更多的类型信息。下面的代码则工作得很好:
listli = new arraylist();
(new integer(42));
list lu = li;
n((0));
为什么该代码能工作呢?对于 lu,编译器一点都不知道 list 的类型参数的值。但是编译器比较聪明,它可以做一些类型推理。在本例中,它推断未知的类型参数必须扩展 object。(这个特定的推理没有太大的跳跃,但是编译器可以作出一些非常令人佩服的类型推理,后面就会看到(在底层细节一节中)。所以它让您调用 () 并推断返回类型为 object。 另一方面,下面的代码不能工作:
listli = new arraylist();
(new integer(42));
list lu = li;
(new integer(43)); // error
在本例中,对于 lu,编译器不能对 list 的类型参数作出足够严密的推理,以确定将 integer 传递给 () 是类型安全的。所以编译器将不允许您这么做。
以免您仍然认为编译器知道哪些方法更改列表的内容哪些不更改列表内容,请注意下面的代码将能工作,因为它不依赖于编译器必须知道关于 lu 的类型参数的任何信息:
listli = new arraylist();
(new integer(42));
list lu = li;
();
4.6 泛型方法
(在 类型参数 一节中)您已经看到,通过在类的定义中添加一个形式类型参数列表,可以将类泛型化。方法也可以被泛型化,不管它们定义在其中的类是不是泛型化的。
泛型类在多个方法签名间实施类型约束。在 list中,类型参数 v 出现在 get()、add()、contains() 等方法的签名中。当创建一个 map类型的变量时,您就在方法之间宣称一个类型约束。您传递给 add() 的值将与 get() 返回的值的类型相同。
类似地,之所以声明泛型方法,一般是因为您想要在该方法的多个参数之间宣称一个类型约束。例如,下面代码中的 ifthenelse() 方法,根据它的第一个参数的布尔值,它将返回第二个或第三个参数:
publict ifthenelse(boolean b, t first, t second) {
return b ? first : second;
}
注意,您可以调用 ifthenelse(),而不用显式地告诉编译器,您想要 t 的什么值。编译器不必显式地被告知 t 将具有什么值;它只知道这些值都必须相同。编译器允许您调用下面的代码,因为编译器可以使用类型推理来推断出,替代 t 的 string 满足所有的类型约束: string s = ifthenelse(b, "a", "b");
类似地,您可以调用:
integer i = ifthenelse(b, new integer(1), new integer(2));
但是,编译器不允许下面的代码,因为没有类型会满足所需的类型约束:
string s = ifthenelse(b, "pi", new float(3.14));
为什么您选择使用泛型方法,而不是将类型 t 添加到类定义呢?(至少)有两种情况应该这样做:
当泛型方法是静态的时,这种情况下不能使用类类型参数。
当 t 上的类型约束对于方法真正是局部的时,这意味着没有在相同类的另一个方法签名中使用相同类型 t 的约束。通过使得泛型方法的类型参数对于方法是局部的,可以简化封闭类型的签名。
4.7 有限制类型
在前一屏 泛型方法 的例子中,类型参数 v 是无约束的或无限制的类型。有时在还没有完全指定类型参数时,需要对类型参数指定附加的约束。
考虑例子 matrix 类,它使用类型参数 v,该参数由 number 类来限制:
public class matrix{ ... }
编译器允许您创建 matrix或 matrix类型的变量,但是如果您试图定义