泛型(Generic)





  • 什么是泛型?

java5开始出现的一种对Java语言类型的一种拓展,以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数类型时指定的类型占位符,就好比方法的形式参数是实际参数的占位符一样。泛型能保证大型应用程序的类型安全和良好的维护性。

上面的官方解释有点太官方,换成自己的话我觉得就是2点:

1,解决元素存储的安全性问题

2,解决获取数据元素时,不需要类型强转。具体代码如下:

package tz.web.main;

import java.util.List;
import com.google.common.collect.Lists; /**
*
* @version 1L
* @author LinkinPark
* @since 2014-12-7
* @motto 梦似烟花心似水,同学少年不言情
* @desc ^ 没有使用泛型
*/
public class Linkin
{
public static void main(String[] args)
{
//@SuppressWarnings("rawtypes") 这个压制警告,就是说原始的数据结构类型,也就是说没有加入泛型检查
//首先这里数据不安全,我本来是要想放字符串进集合的,结果不小心放了数字了,丫的编译不会有问题的
List list = Lists.newArrayList();
list.add("1");
list.add(2);
//在下面的代码
for (Object object : list)
{
//不加入泛型,下面的代码的类型默认都是object的,所以在使用的时候,一般都会强转
String str = (String) object;
//java.lang.Integer incompatible with java.lang.String
//在编译的时候,也是没有问题的,但是实际运行的时候,发生问题了,类型转换错误。除非你每次都来判断具体的类型:object instanceof String
System.out.println(str);
}
}
}
package tz.web.main;

import java.util.List;
import com.google.common.collect.Lists; /**
*
* @version 1L
* @author LinkinPark
* @since 2014-12-7
* @motto 梦似烟花心似水,同学少年不言情
* @desc ^ 使用了泛型
*/
public class Linkin
{
public static void main(String[] args)
{
List<String> list = Lists.newArrayList();
list.add("1");
//你要是这里放入list中的对象的类型不对的话,编译就不通过
//list.add(2);
//在迭代循环list的时候,也不需要自己来每次强转类型了
for (String string : list)
{
System.out.println(string);
}
}
}

总结:使用泛型的优势:

1,类型安全,使编译器对泛型定义的类型做判断限制.如保证TreeSet里的元素类型必须一致

2,消除强制类型的转换,如,使用Comparable比较时每次都需要类型强转。



  • 泛型类

在类声明时通过一个标识符表示类中某个字段的类型或者某个方法的返回值或参数的类型,这样在类声明或实例化的时候只要指定自己需要的类型就ok。

声明带泛型的类:

class 类名<泛型类型1,泛型类型2……>{

泛型类型  变量名;

泛型类型  方法名(){}

返回值类型 方法名(泛型类型 变量名){}

}

使用带泛型的类:

类名<具体类> 对象名 = new 类名<具体类>();

类型参数规范:推荐使用规范-常见的泛型,泛型只保存在源文件中,class文件中不存在;也就是说在编译阶段就会丢失,基本数据类型不能作为泛型类型;

K 键,比如映射的键  key的类型

V 值,比如Map的值 value类型

E 元素,比如Set<E>  Element表示元素,元素的类型

T 泛型,Type的意思

T只能是类,不能用基本数据类型填充。

关于泛型类,主要注意一下几点:

1.对象实例化时不指定泛型,默认为:Object。

2.泛型不同的引用不能相互赋值。

3.加入集合中的对象类型必须与指定的泛型类型一致。

4.静态方法中不能使用类的泛型。

5.如果泛型类是一个接口或抽象类,则不可创建泛型类的对象。

6.不能在catch中使用泛型

7.从泛型类派生子类,泛型类型需具体化

package tz.web.main;

/**
*
* @version 1L
* @author LinkinPark
* @since 2014-12-7
* @motto 梦似烟花心似水,同学少年不言情
* @desc ^ 自定义泛型类
*/
public class Linkin<T>
{
private T name;
private T andress; //以下是2个构造器,注意了:在创建带泛型声明的自定义类时,构造器还是原来的构造器,没有变化的话。变化的只是在使用的过程中,为泛型形参传入实际的类型参数。
public Linkin()
{ }
public Linkin(T name, T andress)
{
this.name = name;
this.andress = andress;
} public T getName()
{
return name;
} public void setName(T name)
{
this.name = name;
} public T getAndress()
{
return andress;
} public void setAndress(T andress)
{
this.andress = andress;
}<p><span style="color:blue;"><span style="white-space:pre"> </span>//</span><span style="color:blue;">static</span><span style="color:blue;">的方法中不能声明泛型</span></p><p><span style="color:#C00000;"><span style="white-space:pre"> </span>//public </span><span style="color:#C00000;">static void show(T t){</span></p><p><span style="color:#C00000;"><span style="white-space:pre"> </span>//}</span></p> public static void main(String[] args)
{
Linkin<String> linkin = new Linkin<String>();
System.out.println(linkin.getAndress());
}
}

若一个类中多个字段需要不同的泛型声明,则在声明类的时候指定多个泛型类型即可。

public interface Map<K,V> {
Set<Map.Entry<K, V>> entrySet();
}
  • 通配符

在进行引用传递的时候泛型类型必须匹配才可以传递,否则编译不通过;使用 ? ,表示未知类型的泛型对象:

List<?> 表示未知元素的List集合;这种带通配符的List仅表示各种泛型List的父类,并不能把元素添加入集合中。

package tz.web.main;

import java.util.ArrayList;
import java.util.List; /**
*
* @version 1L
* @author LinkinPark
* @since 2014-12-7
* @motto 梦似烟花心似水,同学少年不言情
* @desc ^编译器无法基于信息作类型推断
*/
public class Linkin
{
//表示可接受任意类型的List集合
public void show(List<?> list)
{ } public static void main(String[] args)
{
//这种带通配符的List仅仅表示他是各种泛型List的父类,并不能把元素添加到其中。
List<?> list = new ArrayList<Linkin>();
//编译错误 因为上面使用了通配符,这个list就不知道往他里面添加的是什么类型的对象,所以就不能丢进去
//list.add("111");
//这里编译通过,null是唯一的例外。
list.add(null);
}
}
  • 泛型的上限与下限

设置泛型对象的上限使用extends,表示参数类型只能是该类型或该类型的子类(或者是该接口的实现类),

声明对象:类名<? extends 类> 对象名

定义类:类名<泛型标签 extends 类>{}

注意了:与类同时继承父类,实现接口相似:为类型形参指定多个类型的时候,所有的接口上线必须位于类上限之后。





设置泛型对象的下限使用super,表示参数类型只能是该类型或该类型的父类:

声明对象:类名<? super 类> 对象名称

定义类:类名<泛型标签 extends类>{}

注意了:不能同时设置上限和下限

package tz.web.main;

public class Linkin<T extends Number>
{ public static void main(String[] args)
{
//传入的实际类型比如是Number的父类的子类,或者是接口的实现类。
Linkin<Integer> linkin1 = new Linkin<Integer>();
Linkin<Long> linkin2 = new Linkin<Long>();
//Bound mismatch: The type String is not a valid substitute for the bounded parameter <T extends Number> of the type Linkin<T>
//Linkin<String> linkin3 = new Linkin<String>();
}
}
  • 泛型接口

java5后,可以声明泛型接口,声明方式和声明泛型类是一样的。public interface IDAO<T>{}

泛型接口子类有两种方式:1,直接在子类后申明泛型;2,在子类实现的接口中给出具体的泛型类型

public class DaoImpl<T> implements IDAO<T>{



}





推荐使用这种方式,一般都是定义一个泛型接口,然后里面每个具体的实现类的类型都基本确定下来了。上面的那种情况里面的实现类其实也是泛型的,一般不会写这种情况的代码。

public class DaoImpl implements IDAO<String> {



}



  • 泛型方法

方法中可定义泛型参数,形参的参数类型就是实参的类型。声明泛型的标识符,只能在方法返回值类型前。

格式:

<泛型标签> 返回值类型 方法名([泛型标签 参数]...)

public static <T> T show(T param){

return param;

}

package tz.web.main;

public class Linkin
{ public static <T> T show(T param){
return param;
} public static void main(String[] args)
{
System.out.println(Linkin.show("LinkinPark..."));
}
}
  • 泛型方法和类型通配符的区别:

大多数情况下可以使用泛型方法来代替类型通配符。比如:

public void test(List<?> list);就可以写成 public <T> void test(List<T> list)

1,如果方法中的类型形参只使用了一次,类型形参的唯一效果就是可以在不同的调入点传入不同的实际类型,那么这个时候应该使用通配符,通配符就是被设计用来支持灵活子类的。如果方法中的类型形参表示一个或者多个参数之间的依赖关系,或者是说表示方法的返回值和方法参数之间的关系,就应该使用泛型方法。简单的说:使用一次,用通配符,使用了好多次呢,用泛型方法。

2,类型通配符即可以在方法签名中定义形参的类型,也可以用于定义变量的类型。但是泛型方法中类型形参必须在对应方法中显式申明。





  • 泛型的嵌套

可以从一个类的泛型中指向另一个类的泛型。

package tz.web.main;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set; public class Linkin
{ public static void main(String[] args)
{
Map<String,String> map = new HashMap<String,String>();
map.put("1", "A");
map.put("2", "B");
map.put("3", "C");
map.put("4", "D"); //循环map的2种方式
System.out.println("===============第一种===============");
Set<String> keySet = map.keySet();
for(Iterator<String> it = keySet.iterator();it.hasNext();)
{
String key = it.next();
System.out.println(key + "-->" + map.get(key));
}
System.out.println("===============第二种===============");
Set<Map.Entry<String, String>> set = map.entrySet();
Iterator<Map.Entry<String, String>> it = set.iterator();
while(it.hasNext())
{
Map.Entry<String, String> entry = it.next();
System.out.println(entry.getKey() + "-->" + entry.getValue());
}
}
}



  • 泛型的擦除

在严格的泛型代码里,带泛型声明的类总应该带着类型参数。但是为了和老的Java代码保持一致,也允许在使用带泛型声明的类时不指定类型参数,若没有为这个泛型类指定类型参数则该类型参数被称做一个原始类型,默认是该声明参数时指定的最上限类型;当把一个具有泛型信息的对象赋给另一个没有泛型信息的变量时,则所有在尖括号之间的类型信息都被扔掉。

比如List<String> 类型转换成List,则该List对集合元素的类型检查变成了变量的上限即Object。

package tz.web.main;

import java.util.ArrayList;
import java.util.List; public class Linkin<T extends Number>
{
private T t;
public Linkin(T t)
{
this.t = t;
}
public T getT()
{
return t;
}
public void setT(T t)
{
this.t = t;
}
public static void main(String[] args)
{
Linkin<Integer> n = new Linkin<Integer>(5);
Integer i1 = n.getT();
Linkin n2 = n;//会丢掉泛型信息
Number num = n2.getT();
//下面这行代码比如要强转了
Integer i2 = (Integer) n2.getT(); List<Integer> intList = new ArrayList<Integer>();
intList.add(1);
//List<String> strList = null;intList = strList;//不能转换
List list = intList;
List<String> strList = list;//不会报错,只有未经检查警告,此时list实际引用的是List<Integer>
//java.lang.Integer incompatible with java.lang.String
System.out.println(strList.get(0));//企图当做String类型对象取出
//下面这行代码和上面演示的效果一模一样
//System.out.println((String) intList.get(0));
}
}
  • 泛型和数组

只能申明List<String>[]形式的数组,但是不能创建ArrayList<String>[10]这样子的数组。因为他违反了java泛型的设计原则:如果一段代码在编译时系统没有产生[unchecked]未检查警告,程序在运行中就不会发生“ClassCastException”。了解下就好了,一般用不到的。

  • 泛型开发实例

泛型接口 GenericDAO 包含一个常见的数据操作:增删改查

泛型接口实现类 GenericDAOImpl 实现泛型接口里的所有抽象方法





public interface IGenericDAO<T> {

T get(Serializable id);

T save(T newInstance);

void remove(Serializable id);

void update(T object);

}





public class GenericDAOImpl<T> implements IGenericDAO<T>{

public T get(Serializable id) {

return null;

}

public T save(T newInstance) {

return null;

}

public void remove(Serializable id) {

}

public void update(T object) {

}

public List<T> query() {

return null;

}

}



  • 特别需要注意的地方:

1,并不存在泛型类。系统并不会为ArrayList<String>生成新的class类,也不会把它当做新的类来处理。也就是说泛型只是存在与编译阶段,实际上在运行生成的class文件时一样的,那么我们就可以通过跑反射来避开泛型。

 2,在敲代码的过程中,只要是没有出现未检查的警告,那么程序在运行的时候肯定是不会发生类型转换异常的。

 3,在写泛型的方法时,要是只是泛型的类型参数不同,那么这2个方法就算是同一个方法。因为编译后的泛型就去掉了,所以当然是一个方法

public static void  show(List<? extends Number> l){

}

public static void  show(List<? super String> l){

}

4,静态方法中不能使用类的泛型。不能用基本类型实例化泛型类型参数,例如List<int> linkin = null ; 编译报错。



5,泛型和继承的关系:如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,G<B>并不是G<A>的子类型!比如:String是Object的子类,但是List<String >并不是List<Object>的子类。关于上面这点,要和数组区分开来:[B] b是[A] a的子类,但是在泛型接口不行的。

6,对象实例化时不指定泛型,默认为:Object。泛型不同的引用不能相互赋值。

7,从泛型类派生子类,泛型类型需具体化。其实泛型最大的用处就是用来定义一个泛型DAO,里面实现基本的CRUD方法,所以在继承的时候就直接传入实际的类型好了。关于泛型dao,在整理完反射后会统一整理。

linkin大话数据结构--泛型的更多相关文章

  1. linkin大话数据结构--Collection和Iterator

    linkin大话数据结构--Collection和Iterator Java 集合就像一种容器,可以把多个对象的引用放入容器中.Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系 ...

  2. linkin大话数据结构--字符串,数组,list之间的互转

    在实际开发中,我们经常会用到字符串,字符数组,字符list,当然也会不可避免的进行这3者之间的互相转换. 在使用到Apache和Google下的common包,可以这样子实现: package tz. ...

  3. linkin大话数据结构--Google commons工具类

    package tz.web.dao.bean; import java.util.Arrays; import java.util.Collection; import java.util.List ...

  4. linkin大话数据结构--数组

    数组概述:如何正确理解数组?数组也是一种类型 数组是多个相同类型数据的组合,实现对这些数据的统一管理.数组属引用类型,数组型数据是对象(Object),数组中的每个元素相当于该对象的成员变量数组中的元 ...

  5. linkin大话数据结构--Collections类

    操作集合的工具类:Collections Collections 是一个操作 Set.List 和 Map 等集合的工具类.Collections 中提供了大量方法对集合元素进行排序.查询和修改等操作 ...

  6. linkin大话数据结构--Queue

    链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer).由于不必按顺序存储,所以插入和删除速度超 ...

  7. linkin大话数据结构--List

    List:Collection子接口 List是有序的集合,集合中每个元素都有对应的顺序序列.List集合可使用重复元素,可以通过索引来访问指定位置的集合元素(顺序索引从0开始),List集合默认按元 ...

  8. linkin大话数据结构--apache commons工具类

    Apache Commons包含了很多开源的工具,用于解决平时编程经常会遇到的问题,减少重复劳动. 一.Commons BeanUtils 说明:针对Bean的一个工具集.由于Bean往往是有一堆ge ...

  9. linkin大话数据结构--Map

    Map 映射关系,也有人称为字典,Map集合里存在两组值,一组是key,一组是value.Map里的key不允许重复.通过key总能找到唯一的value与之对应.Map里的key集存储方式和对应的Se ...

随机推荐

  1. Qt 之 qwt 和 qwtpolar

    1  Qwt Qwt 全称为 Qt Widgets for Technical Applications,用于专业技术领域的可视化显示,如下所示: 左图为自动控制领域,二阶系统的频率响应:中图为德国小 ...

  2. SQL 语句优化方法

    尽量避免非操作符的使用,在索引上使用 NOT,<> 等操作符,数据库管理系统是不会使用索引的,可以将查询语句转化为可以使用索引的查询. 避免对查询的列的操作,任何对列的操作都可能导致全表扫 ...

  3. Android4.0 声卡配置-高通msm8916移植

    一个正常的UAC设备插入Android 7.0是默认打开UAC配置的,打印的log如下: [ - using xhci_hcd [ - [ -, Product=, SerialNumber= [ - ...

  4. 关于java解析xml文件出现的问题

    DOM解析xml文件 问题1:导入javax.xml.parsers.DocumentBuilderFactory出现问题,如图: 解决办法是:由于创建工程时有个默认的jre,重新创建工程改掉就解决了 ...

  5. CCNA笔记(2)

    CCNA第一天笔记:何为因特网?答:因特网,是连接各台pc与终端设备,正是因为有了因特网,我们才能与全世界交流,玩在线游戏,在线学习.因特网给我们教育带来什么方便?答:没有了地域的阻止,可以在线学习, ...

  6. BZOJ1078: [SCOI2008]斜堆

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1078 每一次进入的点一定是一个极左节点,然后将它所处在的整棵树左右翻转.加上一些情况的处理. ...

  7. CSS3动画属性和flex弹性布局各个属性

    [CSS3动画的使用] 1.声明一个关键帧(动画): @keynames name{ from{} to{} } 每个阶段的写法: ①可以直接使用from-to的写法 ②可以设置0%-100%的写法, ...

  8. [Golang]一道考察defer与命名返回值的题目

    题目 输出: 4 1 3 解释 当函数有可命名结果形参时,结果形参的初始值被设置为零值,函数的return语句会设置结果形参的值 当函数有可命名结果形参时,defer函数是可以修改它,然后再将它的值返 ...

  9. 使用vue-axios请求geoJson数据报错的问题

    最近的项目用到了echarts一个带有散点地图的图表,按照正常jquery写法应该使用ajax请求geojson的数据动态去切换地图,就像下面这样 $.get('Js/map/' + cityData ...

  10. light oj 1184 Marriage Media

    题目: You run a marriage media. You take some profiles for men and women, and your task is to arrange ...