一、概述
1.泛型用来解决什么问题?
使用集合类不加<>也可以,但是使用的时候需要注意类型问题,运行时可能出错;而且其后还要进行各种上下转型。
因此,当操作的引用数据类型不确定的时候。那就使用<>。将要操作的引用数据类型传入即可。其实<>就是一个用于接收类型的参数范围。
总的来说,在程序中,只要用到了带有<>的类或者接口,就要明确传入的具体引用类型。
问题:什么是引用数据类型?
2.好处
- 1,将运行时期的问题ClassCastException转到了编译时期。
- 2,避免了强制转换的麻烦。
3.擦除和补偿(感觉怪怪的,不知道1.8是不是更新了)
3.1.擦除
- 泛型技术是给编译器使用的技术,用于编译时期。确保了类型的安全。
- 运行时,会将泛型去掉,生成的class文件中是不带泛型的,这个称为泛型的擦除。
为什么擦除呢?
据说->因为为了兼容运行时的类的加载器
3.2.补偿
在运行时,通过获取元素的类型进行转换动作。不用使用者在强制转换了。
二、泛型类
可不可以定义一个工具类,来操作所有对象?这就引入了我们的泛型最重要的应用——泛型类。
在jdk1.5后,使用泛型来接收类中要操作的引用数据类型。
泛型类。什么时候用?当类中的操作的引用数据类型不确定的时候,就使用泛型来表示。
使用:
public class Main {
public static void main(String[] args){
GenericDemo<Main> mm = new GenericDemo<>();
mm.set(new Main());
System.out.println(mm.get());
}
}
class GenericDemo<T> {
private T tt;
public T get(){
return tt;
}
public void set(T obj){
tt = obj;
}
}
三、泛型方法
//将泛型定义在方法上,返回值的前面修饰符的后边
public T void show(T tt){
sout(tt.toString());
}
当方法静态时,不能使用类上定义的泛型。如果静态方法使用泛型,只能像上面这样将泛型定义在方法上。
发现了缺点:能实现的方法知识Object类的方法
四、泛型接口
interface inter<T>{
public void show(T tt);
}
class InterDemo implements Inter<String>{
public void show(String str){
sout(str);//sout是简写
}
}
学会了IDEA重构函数快捷键(选中代码块,按下ctrl+alt+M)
~~~~~~~~~~~~~~~~~~~~补充要点:泛型通配符:?~~~~~~~~~~~~~~~~~~~~~~
public static void printCollection(Collection<?> a){//通配符的使用
Iterator<?> it = a.iterator();
while(it.hasNext()){
sout(it.next());
}
}
和下面这个有啥区别呢?
public static <E>void printCollection(Collection<E> a){
Iterator<E> it = a.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
T可以直接拿来当做类来调用方法,?不可以。还有什么区别????可以接着往下看:
通配符的高级用法:
看个例子:
class GenericDemo<T> {
private T tt;
public T get(){
return tt;
}
public void set(T obj){
tt = obj;
}
//其中A是一个类
public static void printCollection(Collection<? extends A> a){//注意这里
Iterator<? extends A> it = a.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~这就引出了泛型限定~~~~~~~~~~~~~~~~~~~~~~~~~~~
五、泛型限定
1.上限
限定其继承自哪里。就是我们上面刚用到的例子。格式就是下面这样,这里就不再举例。
? extends E;//接收E类型或其子类型。因此叫做上限
什么时候用?
Collection里面的addAll方法: bool addAll(Collection<? extends E> c)表示可以用E类型或其子类。
一般存储元素时使用上限。因为这样取出都是按照上线类型来运算的。不会出现类型安全隐患。
2.下限
? super E;//接收E类型或其父类型。这种叫做下限。
下限什么时候用?
TreeSet中的构造函数:TreeSet(Compator<? super E> compactor)表示根据指定比较器进行排序。
对集合中的元素做取出操作时可以使用下限。用的不多但是不好理解!!!
六、集合查阅技巧
在选择前思考一下:
需要唯一吗?
需要:Set
需要制定顺序:
需要: TreeSet
不需要:HashSet
但是想要一个和存储一致的顺序(有序):LinkedHashSet
不需要:List
需要频繁增删吗?
需要:LinkedList
不需要:ArrayList
如何记录每一个容器的结构和所属体系呢?
看名字!
后缀名就是该集合所属的体系。
List
|--ArrayList
|--LinkedList
Set
|--HashSet
|--TreeSet
前缀名就是该集合的数据结构。
- 看到array:就要想到数组,就要想到查询快,有角标。
- 看到link:就要想到链表,就要想到增删快,就要想要 add get remove+frist last的方法。
- 看到hash:就要想到哈希表,就要想到唯一性,就要想到元素需要覆盖hashcode方法和equals方法。
- 看到tree:就要想到二叉树,就要想要排序,就要想到两个接口Comparable,Comparator。
而且通常这些常用的集合容器都是不同步的。