十:Java之泛型
【定义】
一、泛型的定义主要有下面两种:
在程序编码中一些包括类型參数的类型,也就是说泛型的參数仅仅能够代表类。不能代表个别对象。(这是当今较常见的定义)
在程序编码中一些包括參数的类。其參数能够代表类或对象等等。
(如今人们大多把这称作模板)
不论使用那个定义。泛型的參数在真正使用泛型时都必须作出指明。
泛型类。是在实例化类的时候指明泛型的详细类型。
泛型方法。是在调用方法的时候指明泛型的详细类型。
二、使用泛型的目的:
一些强类型程序语言支持泛型,其主要目的是加强类型安全及降低类转换的次数,但一些支持泛型的程序语言仅仅能达到部份目的。
泛型程序设计(Genericprogramming)意味着编写的代码能够被非常多不同类型的对象所重用。
是对java语言的类型系统的一种扩展,以支持创建能够按类型进行參数化的类。能够把类型參数看作是使用參数化类型时指定的类型的一个占位符。就像方法的形式參数是执行时传递的值得占位符一样。
【Java泛型的几种类型代码】
一、不使用泛型的代码
我们定义一个Person类,包括三个属性x。y,z。在我们開始定义地时候,我们也不知道这三个属性是用来干什么的,所以我们定义为Object类型。
可是在使用的时候,我们分别对x。y。z赋予了int,double。String类型,所以在取出的时候。我们须要把这三个类型值进行强制转换。例如以下代码:
1. Person.java
<span style="font-size:18px;"> public class Person {
private Object x;
private Object y;
private Object z;
//使用Object类型。能够转化为不论什么类型
public Object getX() {
return x;
}
public void setX(Object x) {
this.x = x;
}
public Object getY() {
return y;
}
public void setY(Object y) {
this.y = y;
}
public Object getZ() {
return z;
}
public void setZ(Object z) {
this.z = z;
}
} 2. NoGenericTest.java public class NoGenericTest {
public static void main(String[]args){
Person boy=new Person();
boy.setX(20);
boy.setY(22.2);
boy.setZ("帅哥TT");
//这里依据设置的不同类型的值,我们须要进行强制类型转化。 int x=(Integer)boy.getX();
double y=(double)boy.getY();
String z=(String)boy.getZ(); System.out.println(x);
System.out.println(y);
System.out.println(z);
}
} 3. 执行结果
20
22.2
帅哥TT</span>
二、使用一个类型变量泛型的代码
我们定义一个泛型类Person,定义三个属性x,y,z,在測试类中,我们设置属性的值,并打印。
<span style="font-size:18px;"> 1. Person.java
public class Person<T> {
private T x;
private T y;
private T z;
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
public T getZ() {
return z;
}
public void setZ(T z) {
this.z = z;
}
} 2. GenericTest.java public class GenericTest {
public static void main(String[]args){
Person boy=new Person();
boy.setX(20);
boy.setY(22.2);
boy.setZ("帅哥TT");
//不用进行类型转化
System.out.println(boy.getX());
System.out.println(boy.getY());
System.out.println(boy.getZ());
}
} 3. 执行结果
20
22.2
帅哥TT
三、使用两个类型变量泛型的代码
我们定义一个泛型类Person,定义两个属性x,y。使用了两种不同的类型变量,在測试类中。我们设置属性的值。并打印。 1. Person.java public class Person<T1,T2> {
private T1 x;
private T2 y;
public T1 getX() {
return x;
}
public void setX(T1 x) {
this.x = x;
}
public T2 getY() {
return y;
}
public void setY(T2 y) {
this.y = y;
}
} 2. GenericTest.java public class GenerricTest {
public static void main(String[] args){
Person<String,Integer> boy=new Person<String,Integer>();
boy.setX("帅哥TT");
boy.setY(20);
System.out.println(boy.getX());
System.out.println(boy.getY());
} }
3. 执行结果
帅哥TT
20</span>
四、使用泛型的继承
我们定义一个泛型类Person,定义两个属性x。y,然后定义还有一个泛型类Boy。定义属性z。Boy继承Person类,在測试类中,我们设置属性的值。并打印。
1. Person.java public class Person<T1,T2> {
private T1 x;
private T2 y;
public T1 getX() {
return x;
}
public void setX(T1 x) {
this.x = x;
}
public T2 getY() {
return y;
}
public void setY(T2 y) {
this.y = y;
}
} 2. Boy public class Boy<T1,T2,T3>extendsPerson<T1,T2> {
private T3 z;
public T3 getZ() {
return z;
}
public void setZ(T3 z) {
this.z = z;
}
} 3. GenericTest.java 1public class GenericTest {
2 public static void main(String[] args){
3 Boy<String,Integer,Double> boy=new Boy<String,Integer,Double>();
4 boy.setX("帅哥TT");
5 boy.setY(20);
6 boy.setZ(200000.22);
7
8 System.out.println(boy.getX());
9 System.out.println(boy.getY());
10 System.out.println(boy.getZ());
11 }
12 } 4.执行结果
1 帅哥TT
2 20
3 200000.22
五、使用泛型的接口
我们定义一个泛型接口Person,定义两个方法,然后定义还有一个泛型类Boy,实现泛型接口Person。定义属性x,y,z,在測试类中。我们设置属性的值,并打印。
<span style="font-size:18px;">1. Person.java 1 public interface Person<T1,T2> {
2 public T1 getX();
3 public T2 getY();
4 }
2. Boy 1public class Boy<T1,T2,T3>implements Person<T1,T2> {
2 private T1 x;
3 private T2 y;
4 private T3 z;
5 public T1 getX() {
6 return x;
7 }
8 public void setX(T1 x) {
9 this.x = x;
10 }
11 public T2 getY() {
12 return y;
13 }
14 public void setY(T2 y) {
15 this.y = y;
16 }
17 public T3 getZ() {
18 return z;
19 }
20 public void setZ(T3 z) {
21 this.z = z;
22 }
23
24 } 3. GenericTest.java 1public class GenericTest {
2 public static void main(String[] args){
3 Boy<String,Integer,Double> boy=newBoy<String,Integer,Double>();
4 boy.setX("帅哥TT");
5 boy.setY(20);
6 boy.setZ(200000.22);
7 System.out.println(boy.getX());
8 System.out.println(boy.getY());
9 System.out.println(boy.getZ());
10 }
11 } 4. 执行结果
1 帅哥TT
2 20
3 200000.22</span>
六、使用泛型方法
说明一下。定义泛型方法时,必须在返回值前边加一个<T>。来声明这是一个泛型方法,持有一个泛型T,然后才干够用泛型T作为方法的返回值。
定义一个普通类Person,定义一个泛型方法,例如以下代码:
<span style="font-size:18px;">1. Person.java 1public class Person{
2 public static<T>T getMiddle(T[]a){
3 return a[a.length/2];
4 }
5 public static void main(String [] args){
6 String[]name={"帅哥TT","帅哥TT1","帅哥TT2"};
7 String middle=Person.<String>getMiddle(name);
8 System.out.println(middle);
9
10 Integer[]num={20,22,25};
11 Integer middle1=Person.<Integer>getMiddle(num);
12 System.out.println(middle1);
13
14 Double[]num1={20.0,22.2,25.5};
15 Double middle2=Person.<Double>getMiddle(num1);
16 System.out.println(middle2);
17 }
18 } 2. 执行结果 1 帅哥TT1
2 22
3 22.2</span>
七、类型变量的限定
例如以下代码,我们在方法min中定义了一个变量smallest类型为T。这说明了smallest能够是不论什么一个类的对象,我们在以下的代码中须要使用compareTo方法, 可是我们没有办法确定我们的T中含有CompareTo方法,所以我们须要对T进行限定,在代码中我们让T继承Comparable类。
例如以下:
<span style="font-size:18px;">public static<T extendsComparable>T min(T[]a)
1.Person.java
1public class Person{
2 public static<T extends Comparable>T min(T[]a){
3 if(a==null||a.length==0){
4 return null;
5 }
6 T smallest=a[0];
7 for(int i=1;i<a.length;i++){
8 if(smallest.compareTo(a[i])>0){
9 smallest=a[i];
10 }
11 }
12 return smallest;
13 }
14 public static void main(String [] args){
15 Integer[]num={20,25,30,10};
16 Integer middle=Person.<Integer>min(num);
17 System.out.println(middle);
18 }
19 } 2. 执行结果
10</span>
【Java泛型理解】
一、类型擦除
正确理解泛型概念的首要前提是理解类型擦除(type erasure)。 Java中的泛型基本上都是在编译器这个层次来实现的。
在生成的Java字节代码中是不包括泛型中的类型信息的。使用泛型的时候加上的类型參数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的List<Object>和List<String>等类型。在编译之后都会变成List。JVM看到的仅仅是List,而由泛型附加的类型信息对JVM来说是不可见的。Java编译器会在编译时尽可能的发现可能出错的地方,可是仍然无法避免在执行时刻出现类型转换异常的情况。
非常多泛型的奇怪特性都与这个类型擦除的存在有关,包含:
泛型类并没有自己独有的Class类对象。比方并不存在List<String>.class或是List<Integer>.class,而仅仅有List.class。
静态变量是被泛型类的全部实例所共享的。对于声明为MyClass<T>的类,訪问当中的静态变量的方法仍然是 MyClass.myStaticVar。
无论是通过new MyClass<String>还是new MyClass<Integer>创建的对象,都是共享一个静态变量。
泛型的类型參数不能用在Java异常处理的catch语句中。
由于异常处理是由JVM在执行时刻来进行的。由于类型信息被擦除,JVM是无法区分两个异常类型MyException<String>和MyException<Integer>的。对于JVM来说,它们都是 MyException类型的。也就无法执行与异常相应的catch语句。
二、最佳实践
在使用泛型的时候能够遵循一些主要的原则。从而避免一些常见的问题。
在代码中避免泛型类和原始类型的混用。比方List<String>和List不应该共同使用。这样会产生一些编译器警告和潜在的执行时异常。当须要利用JDK 5之前开发的遗留代码,而不得不这么做时,也尽可能的隔离相关的代码。
在使用带通配符的泛型类的时候,须要明白通配符所代表的一组类型的概念。因为详细的类型是未知的,非常多操作是不同意的。
泛型类最好不要同数组一块使用。你仅仅能创建new List<?>[10]这种数组。无法创建new List<String>[10]这种。这限制了数组的使用能力。并且会带来非常多费解的问题。因此,当须要类似数组的功能时候,使用集合类就可以。
不要忽视编译器给出的警告信息。
十:Java之泛型的更多相关文章
- [改善Java代码]Java的泛型是类型擦除的
泛型可以减少强制类型的转换,可规范集合的元素类型,还可以提高代码的安全性和可读性,正是因为有了这些优点,自从Java引入泛型之后,项目的编码规则上便多了一条,优先使用泛型. Java泛型(Generi ...
- WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇)
原文:WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]在.NE ...
- Java 中泛型的全面解析(转)
Java泛型(generics) 是JDK 5中引入的一个新特性,允许在定义类和接口的时候使用类型参数(type parameter).声明的类型参数在使用时用具体的类型来替换.泛型最主要的应用是在J ...
- Java中泛型 类型擦除
转自:Java中泛型是类型擦除的 Java 泛型(Generic)的引入加强了参数类型的安全性,减少了类型的转换,但有一点需要注意:Java 的泛型在编译器有效,在运行期被删除,也就是说所有泛型参数类 ...
- Java 泛型 Java使用泛型的意义
Java 泛型 Java使用泛型的意义 @author ixenos 直接意义 在编译时保证类型安全 根本意义 a) 类型安全问题源自可复用性代码的设计,泛型保证了类型安全的复用模板 b) 使用复用性 ...
- 跟着刚哥梳理java知识点——泛型(十三)
一. 泛型概念的提出(为什么需要泛型)? 首先,我们看下下面这段简短的代码: public class GenericTest { public static void main(String[] a ...
- 【Java】泛型学习笔记
参考书籍 <Java核心技术:卷1> 泛型, 先睹为快 先通过一个简单的例子说明下Java中泛型的用法: 泛型的基本形式类似于模板, 通过一个类型参数T, 你可以"私人定制&qu ...
- Java进阶(四十)Java类、变量、方法修饰符讲解
Java进阶(四十)Java类.变量.方法修饰符讲解 Java类修饰符 abstract: 将一个类声明为抽象类,没有实现的方法,需要子类提供方法实现. final: 将一个类生命为最终(即非继承类) ...
- [转] Java 的泛型擦除和运行时泛型信息获取
原文链接 https://my.oschina.net/lifany/blog/875769 前言 现在很多程序员都会在简历中写上精通 Java.但究竟怎样才算是精通 Java 呢?我觉得不仅要熟练掌 ...
随机推荐
- Android乐学成语之自定义Adapter
一.首先对Adapter概念深刻的了解 首先看看他的继承图
- BZOJ 1097: [POI2007]旅游景点atr( 最短路 + 状压dp )
先最短路预处理, 然后状压就行了 -------------------------------------------------------------------------- #include ...
- Spring学习之Aop的基本概念
转自:http://my.oschina.net/itblog/blog/209067 AOP的基本概念 AOP从运行的角度考虑程序的流程,提取业务处理过程的切面.AOP面向的是程序运行中的各个步骤, ...
- ThinkPHP第十九天(Ueditor高亮插件、扩展函数载入load、静态缓存)
1.使用Ueditor编辑器,插入代码后,显示的时候高亮显示,需要调用Ueditor中的第三方插件third-party中的SyntaxHighlighter 调用方法: 引入CSS和JS文件,并调用 ...
- Android 开发笔记“浅谈DDMS视图”
DDMS 的全称是Dalvik Debug Monitor Service,即Dalvik调试监控服务,是一个可视化的调试监控工具.它主要是对系统运行后台日志的监控,还有系统线程,模拟器状态的监控.此 ...
- perl5 第九章 关联数组/哈希表
第九章 关联数组/哈希表 by flamephoenix 一.数组变量的限制二.定义三.访问关联数组的元素四.增加元素五.创建关联数组六.从数组变量复制到关联数组七.元素的增删八.列出数组的索引和值九 ...
- BZOJ 3385: [Usaco2004 Nov]Lake Counting 数池塘
题目 3385: [Usaco2004 Nov]Lake Counting 数池塘 Time Limit: 1 Sec Memory Limit: 128 MB Description 农夫 ...
- Android学习资料PDF免费大放送,每日更新!有需要的亲看这里
说明:以前有资料分享的时候,都叫大家留下自己的QQ邮箱,这样被很多的程序猿们认为我是专门来钓你们的QQ邮箱的.为此给大家带来的顾虑,深表抱歉.从现在开始,以后的分享资料我都会放在自己的百度网盘中,提供 ...
- 使用C#对MongoDB中的数据进行查询,改动等操作
首先,使用的是官方提供的C#訪问组件https://github.com/mongodb/mongo-csharp-driver 然后.编译后引用MongoDB.Bson.dll及MongoDB.Dr ...
- C++模板编程
如何处理函数模板中的函数体? 预备知识补充: 按照c++的语言系统,普通函数及类的声明应该放在一个头文件中(通常是.h. .hpp..hh为扩展名)里: 而将其实现放在一个主代码文件中(通常以.c . ...