linkin大话数据结构--泛型
泛型(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大话数据结构--泛型的更多相关文章
- linkin大话数据结构--Collection和Iterator
linkin大话数据结构--Collection和Iterator Java 集合就像一种容器,可以把多个对象的引用放入容器中.Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系 ...
- linkin大话数据结构--字符串,数组,list之间的互转
在实际开发中,我们经常会用到字符串,字符数组,字符list,当然也会不可避免的进行这3者之间的互相转换. 在使用到Apache和Google下的common包,可以这样子实现: package tz. ...
- linkin大话数据结构--Google commons工具类
package tz.web.dao.bean; import java.util.Arrays; import java.util.Collection; import java.util.List ...
- linkin大话数据结构--数组
数组概述:如何正确理解数组?数组也是一种类型 数组是多个相同类型数据的组合,实现对这些数据的统一管理.数组属引用类型,数组型数据是对象(Object),数组中的每个元素相当于该对象的成员变量数组中的元 ...
- linkin大话数据结构--Collections类
操作集合的工具类:Collections Collections 是一个操作 Set.List 和 Map 等集合的工具类.Collections 中提供了大量方法对集合元素进行排序.查询和修改等操作 ...
- linkin大话数据结构--Queue
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer).由于不必按顺序存储,所以插入和删除速度超 ...
- linkin大话数据结构--List
List:Collection子接口 List是有序的集合,集合中每个元素都有对应的顺序序列.List集合可使用重复元素,可以通过索引来访问指定位置的集合元素(顺序索引从0开始),List集合默认按元 ...
- linkin大话数据结构--apache commons工具类
Apache Commons包含了很多开源的工具,用于解决平时编程经常会遇到的问题,减少重复劳动. 一.Commons BeanUtils 说明:针对Bean的一个工具集.由于Bean往往是有一堆ge ...
- linkin大话数据结构--Map
Map 映射关系,也有人称为字典,Map集合里存在两组值,一组是key,一组是value.Map里的key不允许重复.通过key总能找到唯一的value与之对应.Map里的key集存储方式和对应的Se ...
随机推荐
- lxml的另一种用法
python中lxml库是一个十分强大的xml解析库,最近在看<白帽子将web扫描>这本书的时候,里面提供了一种不同于以往的用法,因此在这将这个方法记录下来 传统的lxml库的使用方法类似 ...
- windows 驱动开发入门——驱动中的数据结构
最近在学习驱动编程方面的内容,在这将自己的一些心得分享出来,供大家参考,与大家共同进步,本人学习驱动主要是通过两本书--<独钓寒江 windows安全编程> 和 <windows驱动 ...
- 浮动(float)与清除浮动(clear)
上一篇中我们了解了块级元素与内联元素,今天将自己查阅的资料整理写出来,与大家一起理解什么是浮动,以及怎样清除浮动,本文以div元素为例. 浮动的框可以向左或向右移动,直到它的外边缘碰到包含框或另一个浮 ...
- InnoDB锁机制
1. 锁类型 锁是数据库区别与文件系统的一个关键特性,锁机制用于管理对共享资源的并发访问. InnoDB使用的锁类型,分别有: 共享锁(S)和排他锁(X) 意向锁(IS和IX) 自增长锁(AUTO-I ...
- npm package.json文件解读
每个Nodejs项目的根目录下面,一般都会有一个package.json文件.该文件可以由npm init生成,定义了项目所需要的各种模块,以及项目的配置信息(比如名称.版本.许可证等元数据). pa ...
- 浅谈我的MongoDB学习(一)
这是第一次写博客,不当之处敬请见谅,最近由于项目需要,对mongodb略有研究,网上也有一些相关资料,下面是我自己摸索的一些东西,希望能跟大家分享一下当然,这也是我自己第一次在项目中使用,若理解有误, ...
- 正则表达式中的 \b 什么意思?
以前经常看到类似这样的正则表达式:\bhi\b 不知道什么意思,今天特意去查了下. 原来\b是正则表达式规定的一个特殊代码,也叫元字符,\b代表着单词的开头或结尾,也就是单词的分界处.
- uboot引导linux内核过程详解【转】
http://blog.chinaunix.net/uid-7828352-id-4472376.html 写的不错,尤其是uboot向linux内核传递参数的过程写的比较详细.
- Two Sum(两个数的相加)
2017.11.10题目描述:Given nums = [2, 7, 11, 15], target = 9, Because nums[0] + nums[1] = 2 + 7 = 9, retur ...
- JSR-303校验类型
空检查 @Null 验证对象是否为null @NotNull 验证对象是否不为null, 无法查检长度为0的字符串 @NotBlank 检查约束字符串是不是Null还有被Trim的长 ...