泛型 --- 通用类型
由来 : java语言 开发者 一批C++ 工程师 ,在c++ 语法中 模板技术 ----- java泛型由来应用 : 1、类型安全检查 2、编写通用java程序(java框架)
JDK5 之前集合对象使用问题: 1、向集合添加任何类型对象 2、从集合取出对象时,数据类型丢失,使用与类型相关方法,强制类型转换
* 程序存在安全隐患
泛型语法: List<泛型类型> 规定 List集合中元素类型 ,取出集合中元素时,获得具体数据类型元素(不需要进行类型强制转换 )
* 泛型技术 只是 编译器阶段技术,为javac命令 起到类型安全检查作用 ,生成.class文件后,泛型信息将会被 擦除
List<String> ---- 参数化类型
泛型技术 对象 List 、Set 、Map中元素 进行类型安全约束
* 掌握 遍历 使用 类型安全 List 、Set 、Map
使用泛型的对象进行类型转换时,等号两端对象使用泛型类型 必须一致!
使用泛型编写一些通用java程序 : 结合反射技术一起使用 ----- 自定义泛型
1、定义泛型方法,必须在方法的返回值之前进行 泛型类型声明 <泛型类型>
* 编写一个通过数组交换元素方法
* 编写一个通过数组倒序方法
2、在类名后声明类的泛型,当类的泛型使用后,该类中所有方法都可以直接使用泛型 ------ 在类名后 <泛型类型> ---- 不对static方法生效
* 对应泛型类型参数起名 T E K V ---- 泛型类型可以以任意大写字母命名,建议你使用有意义的字母
T Template E Element K key V value
泛型通配符 ---- > ?
泛型 : 任意类型
?:任意泛型类型
通过上下边界,限制通配符类型范围:
? extends Number ----> Number任意子类型 (包含Number)
? super String ---- > String任意父类型 (包含String)
* 上下边界不能同时使用
? extends Object super Integer ----- 没有这么写的
package cn.corotata;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.Map.Entry;
import org.junit.Test;
public class GenericTest {
@Test
public void demo11() {
List<?> list = new ArrayList<String>(); // ? 代表任意类型
// list.add("aaa");
// list.add(111);
// 当使用通配符后,不要使用类型相关方法,比如list.add();
List<? extends String> list2 = new ArrayList<String>();
List<? extends Number> list3 = new ArrayList<Integer>();
List<? super String> list4 = new ArrayList<Object>();
}
@Test
public void demo10() {
// 打印数组中所有元素内容
List<String> list = new LinkedList<String>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
print(list);
List<Integer> list2 = new LinkedList<Integer>();
list2.add(111);
list2.add(222);
list2.add(333);
print(list2);
}
// ? 代表任意类型
public void print(List<?> list) { // 泛型类型 可以是任何类型 --- 泛型通配符
for (Object string : list) {
System.out.println(string);
}
}
@Test
public void demo9() {
// 测试 泛型类 使用
/*
* 在创建工具类对象时,使用泛型String, 这个工具类对象完成对String数组 倒序和 交换
*/
ArraysUtils<String> arraysUtils = new ArraysUtils<String>();
String[] arr1 = { "aaa", "bbb", "ccc", "ddd" };
// 交换位置
arraysUtils.changePosition(arr1, 1, 3);
System.out.println(Arrays.toString(arr1));
// 倒序
arraysUtils.reverse(arr1);
System.out.println(Arrays.toString(arr1));
}
@Test
public void demo8() {
// 将数组元素 倒序
String[] arr1 = { "aaa", "bbb", "ccc", "ddd" }; // ddd ccc bbb aaa
reverse(arr1);
System.out.println(Arrays.toString(arr1));
Integer[] arr2 = { 1, 2, 3, 4, 5 }; // 倒序
reverse(arr2);
System.out.println(Arrays.toString(arr2));
}
// 将数组倒序
public <T> void reverse(T[] arr) {
/*
* 只需要遍历数组前一半元素,和后一半元素 对应元素 交换位置
*/
for (int i = 0; i < arr.length / 2; i++) {
// String first = arr[i];
// String second = arr[arr.length - 1 - i];
T temp = arr[i];
arr[i] = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = temp;
}
}
@Test
public void demo7() {
Integer[] arr1 = new Integer[] { 1, 2, 3, 4, 5 };
// 交换数组 两个指定位置元素 将2,4 交换位置
changePosition(arr1, 1, 3); // 使用泛型方法 参数必须是对象类型
System.out.println(Arrays.toString(arr1));
String[] arr2 = new String[] { "aaa", "bbb", "ccc", "ddd" };
// 交换 aaa和 ccc的位置
changePosition(arr2, 0, 2);
System.out.println(Arrays.toString(arr2));
}
// 使用泛型 编写交换数组通用方法,类型可以String 可以 int --- 通过类型
public <T> void changePosition(T[] arr, int index1, int index2) {
T temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
}
// public void changePosition(String[] arr, int index1, int index2) {
// String temp = arr[index1];
// arr[index1] = arr[index2];
// arr[index2] = temp;
// }
//
// // 传递数组 传递数组引用地址 ---- 不需要返回值
// public void changePosition(int[] arr, int index1, int index2) {
// int temp = arr[index1];// 将第一个元素保存到temp
// arr[index1] = arr[index2]; // 将第二个元素值 赋值 给第一个元素
// arr[index2] = temp; // 将temp的值 保存第二个元素
// }
@Test
public void demo6() {
List<Integer> ints = new LinkedList<Integer>(); // 第一句 泛型 两端 一样的
// List<Number> nums = new LinkedList<Integer>(); // 第二句 Number是
// Integer的父类型 ,表达式两端泛型不一致,会导致编译错误
}
@Test
public void demo5() {
// 使用类型安全的Map -- 因为map是一个键值对结构,执行两个类型泛型
Map<String, String> map = new HashMap<String, String>();
map.put("aaa", "111");
map.put("bbb", "222");
// 因为使用了泛型,所以key和value类型都必须为String
// 取出map元素 ----- 两种
// 第一种 通过 Map 的 keySet进行遍历
Set<String> keys = map.keySet(); // 获得key集合
for (String key : keys) {
System.out.println(key + ":" + map.get(key));
}
System.out.println("-------------------------");
// 第二种 通过 map的 entrySet ---- 获得每一个键值对
Set<Map.Entry<String, String>> entrySet = map.entrySet(); // 每一个元素
// 就是一个键值对
for (Entry<String, String> entry : entrySet) {
// 通过 entry的 getKey和getValue获得每一个键和值
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
@Test
public void demo4() {
// 使用类型安全Set
Set<String> set = new TreeSet<String>();
set.add("asd");
set.add("fdf");
set.add("bxc");
// 因为使用泛型 只能添加String类型元素
// 取出Set元素 --- 两种 因为Set是无序的,所以比List少一种遍历方法
// 第一种 继承 Collection 所以使用 Iterator 遍历
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
System.out.println(s);
}
System.out.println("------------------------");
// 第二种 JDK5 引入 foreach 可以使用foreach 遍历 Set
for (String s : set) {
System.out.println(s);
}
}
@Test
public void demo3() {
// 使用类型安全List
List<String> list = new LinkedList<String>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
// 因为使用泛型,只能将list添加 String类型 元素
// 遍历List --- 三种
// 第一种 因为List是有序的(存入顺序和取出顺序一样) 通过 size 和 get方法进行遍历
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}
System.out.println("---------------------------------------------");
// 第二种 因为List 继承 Collection 接口 ,通过 Collection的iterator进行遍历
Iterator<String> iterator = list.iterator();
// 遍历iterator 通过 迭代器 hasNext 和 next 方法进行遍历
while (iterator.hasNext()) {
String s = iterator.next();
System.out.println(s);
}
System.out.println("---------------------------------------------");
// 第三种 JDK5 引入 foreach循环 结构 ,通过foreach结构 遍历 list
for (String s : list) {
System.out.println(s);
}
System.out.println("----------------------------------------------");
}
@Test
public void demo2() {
// 应用泛型 集合
List<String> list = new ArrayList<String>(); // 这个list中只能存放 String 类型数据
// list.add(123); // 只能添加Strring
list.add("abc");
list.add("def");
// 取出集合数据
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s.toUpperCase());
}
}
@Test
public void demo1() {
// JDK5 之前 集合对象
List list = new ArrayList();
// 因为没有泛型类型检查,添加对象可以是任意类型
list.add("abc");
list.add(123);
// 操作集合中对象,遍历集合 将数据取出来 通过 size()方法和 get(index)方法 遍历 list 集合
for (int i = 0; i < list.size(); i++) {
// 取出对象时,数据类型丢失了
Object o = list.get(i);
// 操作 String 方法 ---- 强制将数据转换 相应类型
String s = (String) o;
System.out.println(s.toUpperCase());
}
}
// public static void main(String[] args) {
// // 应用泛型 集合
// List<String> list = new ArrayList<String>(); // 这个list中只能存放 String 类型数据
// // list.add(123); // 只能添加Strring
// list.add("abc");
// list.add("def");
//
// // 取出集合数据
// for (int i = 0; i < list.size(); i++) {
// String s = list.get(i);
// System.out.println(s.toUpperCase());
// }
// }
}
另一个例子来体现泛型的效率:
package cn.corotata;
/**
* 使用类泛型
*
* @author corotata
*
*/
public class ArraysUtils<A> { // 类的泛型
// 将数组倒序
public void reverse(A[] arr) {
/*
* 只需要遍历数组前一半元素,和后一半元素 对应元素 交换位置
*/
for (int i = 0; i < arr.length / 2; i++) {
// String first = arr[i];
// String second = arr[arr.length - 1 - i];
A temp = arr[i];
arr[i] = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = temp;
}
}
public void changePosition(A[] arr, int index1, int index2) {
A temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
}
}
小结 泛型
1、 泛型用来在编译阶段 对集合对象进行类型安全检查 ---- 遍历 类型安全List Set Map
2、 泛型技术 结合 反射 编写通用java程序 ---- 定义通过数组交换位置方法、倒序方法 --- 抽取泛型类中
3、 泛型的通配符 和 上下边界 --- 在javaAPI中有很多应用,了解即可