Java 泛型
本文是学习过程中一些笔记,如有不对之处欢迎指正
我们在使用Java的时候通常会看到Object、Item等字样,它们通常指我们所说的泛型。它相当于客户端使用的某种具体类型的符号占位符,当我们写一个类的时候可能不确定应用这个类的对象 是什么类型的,这样的话我们就可以使用泛型,然后在应用的时候传入相应的参数即可。
在Java的 Tutorials 用BOX 类定义泛型:
下面是Java 的 Tutorials 里面的讲解:
泛型类型是通过类型参数化的泛型类或接口。将修改以下Box类以演示该概念。
首先检查对任何类型的对象进行操作的非泛型Box类。它只需要提供两个方法:set,它将一个对象添加到框中,get,它将检索它:
public class Box {
private Object object;
public void set(Object object) { this.object = object; }
public Object get() { return object; }
}
由于它的方法接受或返回一个Object,所以你可以自由地传入任何你想要的东西,前提是它不是一种原始类型。在编译时无法验证类的使用方式。代码的一部分可能会在框中放置一个Integer并期望从中获取Integers,而代码的另一部分可能会错误地传入String,从而导致运行时错误。(在这里我们传入的只能是引用类型,而不能是int等八种基本类型)
Box类的通用版本
一个泛型类通常被定义为:
class name<T1, T2, ..., Tn> { /* ... */ }
由尖括号(<>)分隔的类型参数部分跟在类名后面。它指定类型参数(也称为类型变量)T1,T2,…和Tn。要更新Box类以使用泛型,可以通过将代码“public class Box”更改为“public class Box ”来创建泛型类型声明。这样我们就引入了自己定义的类型变量T , 并且可以在类中的任何位置使用。
这样的话Box类的重新定义为:
/**
* Box 类的通用类型
* @参数<T>是被封装的类型
*/
public class Box<T> {
// T stands for "Type"
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
如上所示,所有出现的Object都被T替换。类型变量可以是您指定的任何非基本类型:任何类类型,任何接口类型,任何数组类型,甚至是其他类型变量。
可以应用相同的方法创建通用接口。这样我们在一个方法里面就可以引入自己的类型变量,而在实例化的时候我们将这个变量引入就可以了
键入参数命名约定
按照惯例,类型参数名称是单个大写字母。 这与您已经了解的变量命名约定形成了鲜明的对比,并且有充分的理由:如果没有这种约定,就很难区分类型变量和普通类或接口名称。
常用的参数命名
E - Element (通常用于集合中) K - Key
N - Number T - Type
V - Value S,U,V etc. - 2nd, 3rd, 4th types
调用和实例化泛型类型
要从代码中引用通用Box类,必须执行泛型类型调用,它将T替换为某些具体值,例如Integer。
Box<Integer> integerBox;
调用的时候可以将泛型类型调用视为与普通方法调用类似,但不是将参数传递给方法,而是将类型参数(在本例中为Integer)传递给Box类本身。 与任何其他变量声明一样,此代码实际上并不创建新的Box对象。 它只是声明integerBox将保存对“Box of Integer”的引用,这就是Box 的读取方式。
泛型类型的调用通常称为参数化类型。
要实例化此类,请像往常一样使用new关键字,但在类名和括号之间放置:`
Box<Integer> integerBox = new Box<Integer>();
在Java SE 7及更高版本中,只要编译器可以从上下文中确定或推断类型参数,就可以用一组空的类型参数(<>)替换调用泛型类的构造函数所需的类型参数, 这对尖括号<>非正式地称为 diamond。 例如,您可以使用以下语句创建Box 的实例:Box<Integer> integerBox = new Box<>();
如前所述,泛型类可以有多个类型参数。 例如,通用的OrderedPair类,它实现了通用的Pair接口:
public interface Pair<K, V> {
public K getKey();
public V getValue();
}
public class OrderedPair<K, V> implements Pair<K, V> {
private K key;
private V value;
public OrderedPair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
以下语句创建OrderedPair类的两个实例:
Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String> p2 = new OrderedPair<String, String>("hello", "world");
在这里,新的OrderedPair <String,Integer>,将K实例化为String,将V实例化为Integer。 因此,OrderedPair的构造函数的参数类型分别是String和Integer。 由于自动装箱,将String和int传递给类是有效的。
正如The Diamond中所提到的,因为Java编译器可以从声明OrderedPair <String,Integer>推断出K和V类型,所以可以使用<>表示法缩短这些语句:
OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8);
OrderedPair<String, String> p2 = new OrderedPair<>("hello", "world");
参数化类型
还使用参数化类型(即List )替换类型参数(即K或V)。 例如,使用OrderedPair <K,V>示例:
OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));
**
应用
**
以下为为我对泛型的简单应用,在这里我创建了一个顺序表,让它指定类型为E,然后我就可以在类里面定义变量类型为E ,然后我在后面实例化对象的时候给它指定类型,
package sjjg_liuxiaojing_learn;
public class IList<E> {
private Object[] SqList ;
private int curlen ;
IList() {
curlen = 0;
SqList = null;
}
public void add(E i) {
Object[] newcur = new Object[curlen + 1];
for(int j = 0;j < curlen;j++) {
newcur[j] = SqList[j]; //遍历的不是新数组,而是原来的那个旧数组
} //所以才使用curlen作为遍历长度。
curlen++;
newcur[curlen - 1] = i;
SqList = newcur;
}
public void clear() {
curlen = 0;
}
public boolean isEmpty() {
return curlen == 0;
}
public int length() {
return curlen;
}
public Object get(int i) throws Exception{
//得到线性表里面位置i的元素
if(i < 0 || i > curlen - 1)
throw new Exception("第i个元素不存在");
return SqList[i];
}
public void insert(int i,E x)throws Exception {
//在线性表里面第i个位置插入元素
if(i < 0||i > curlen )
throw new Exception("插入位置不合法");
Object[] newList = new Object[curlen + 1];
for(int j = 0;j < curlen + 1;j++) { //还是遍历数组出现问题
if(j < i) //要分清应该遍历那个数组,这个应该是遍历新数组
newList[j] = SqList[j];
else if(j == i)
newList[j] = x;
else
newList[j] = SqList[j - 1];
}
curlen++;
SqList = newList;
}
public void remove(int i)throws Exception{
//删除链表中位置为i的元素
if(i < 0||i > curlen -1)
throw new Exception("删除位置不合法");
for(int j = i;j<curlen -1;j++) //遍历的是新的数组
SqList[j] = SqList[j + 1];
curlen--;
}
public int indexOf(E x) {
for(int i = 0;i<curlen -1;i++) {
if(SqList[i] == x)
return i;
}
return -1;
}
public void display(){
for(int i = 0;i<curlen;i++) //遍历数组为数组的长度
System.out.print(SqList[i] + " ");
System.out.println();
}
//数据结构课后题1:实现线性表顺序逆置操作
public void inverse() {
Object[] newcur = new Object[curlen];
for(int i = 0,j = curlen - 1;i < j;i++,j--) { //空间复杂度低
Object temp = SqList[i];
SqList[i] = SqList[j];
SqList[j] = temp;
}
}
public void turnright(int k) {
Object[] newcur = new Object[curlen];
for(int i= 0;i < curlen;i++) {
newcur[i] = SqList[i]; //第一次出错为不能直接复制,相当于多加一个标签没什么用
if(i < k)
SqList[i] = SqList[curlen - k + i];
else //有时候还是要根据整体来列这个式子
SqList[i] = newcur[i - k];
}
}
}
实例化对象
在这里我实例化对象IList 为L,并且类型设置为Integer ,这样我就可以添加类型为Integer ,并且加入类型为int的时候自动向上转型为Integer
package sjjg_liuxiaojing_learn;
public class IList_shixian {
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
IList<Integer> L = new IList<Integer>();
L.add(3);
L.add(2);
L.add(3);
L.insert(1, 33);
L.add(4);
L.add(3);
for(int i = 0;i<5;i++) { //insert方法出现错误记得改正
L.add(i);
}
L.remove(0);
L.display();
System.out.println(L.length());
System.out.println(L.isEmpty());
System.out.println(L.indexOf(5));
L.inverse();
L.display();
L.turnright(2);
L.display();
}
}
如图所示在for循环里插入int类型,自动转换为Integer.