泛型与泛型类
泛型的本质是类型参数化, 也就是说操作的数据类型被指定为一个参数, 即“类型的变量”。
采用泛型类, 就可以将封装方式相同但是属性类型不同的多个类用一个泛型表示, 减少程序的开发工作量,提高软件的开发效率。
通过定义泛型类,可以将变量的类型作为参数来定义,而变量的具体类型是在创建泛型类的对象时再确定的。
如果想表示任意类型对象的数组,则数组中封装的数组类型应该是Object,但是数组对象中的数组元素可以是不同类型的。如果使用泛型,则可以保证每一个数组类对象中的元素都是同一类型的。
package com.test;
class GenericArray<T> // 泛型类,类型参数名T
{
int n; // 数组的总长度
int total; // 数组的实际长度
private Object arr[]; // Object 类型的对象数组
public GenericArray(int n) // 构造方法
{
this.n = n;
total = 0;
arr = new Object[n];
}
public void add(T obj) // 将一个T 型对象加到数组中
{
arr[total++] = obj;
}
public T indexOf(int i) // 获得第i 个元素,返回值类型是T 型
{
return (T) arr[i]; // 返回前强制转换成T 型
}
public int length() {
return total;
}
}
package com.test;
public class Test {
public static void main(String args[]) {
int i;
String str[] = new String[2]; // 一个String 类的对象数组
str[0] = "Beijing";
str[1] = "Shanghai";
GenericArray<String> arrObj1 = new GenericArray<String>(10);
// 创建一个泛型类对象,所有元素均为String 型
for (i = 0; i < str.length; i++) // 将每个元素加到数组对象中
arrObj1.add(str[i]);
for (i = 0; i < arrObj1.length(); i++) // 输出元素
System.out.println(arrObj1.indexOf(i)); // 获得第i 个元素后直接输出,不用强制类型转换
// -------------------------------------------------
System.out.println("-----------");
Integer intObj[] = new Integer[2]; // 一个Integer 类的对象数组
intObj[0] = new Integer(123);
intObj[1] = new Integer(-456);
GenericArray<Integer> arrObj2 = new GenericArray<>(10);
// 创建一个泛型类对象,所有元素均为Integer 型
for (i = 0; i < intObj.length; i++) // 将每个元素加到数组对象中
arrObj2.add(intObj[i]);
for (i = 0; i < arrObj1.length(); i++) // 输出元素
System.out.println(arrObj2.indexOf(i).intValue());
// 获得第i 个元素后直接输出,不用强制类型转换
// -------------------------------------------------
System.out.println("-----------");
Object obj[] = new Object[2]; // 一个Object 类型的对象数组
obj[0] = "Beijing"; // 第0 个元素是String 型
obj[1] = new Integer(-123); // 第1 个元素是Integer 型
GenericArray<String> arrObj3 = new GenericArray<>(10);
// 创建一个泛型类对象,所有元素均为String 型
arrObj3.add("Beijing");
// arrObj3.add(-123);//泛型类对象arrObj3 只能加String 型对象,写上这行语句,编译不能通过
for (i = 0; i < arrObj3.length(); i++)
System.out.println(arrObj3.indexOf(i));
}
}
输出:
泛型类名[<实际类型>] 对象名=new 泛型类名<>
其中的实际类型不能是基本数据类型,必须是类或者接口。
泛型方法
定义方法时声明了类型参数, 这样的类型参数只限于在该方法中使用。 泛型方法可以定义在泛型类中, 也可以定义在非泛型类中。
package com.test;
import java.util.Date;
public class Test {
public static <T> void print(T t)// 泛型方法
{
System.out.println(t);
}
public static void main(String args[]) {
// 调用泛型方法
print("Apple");
print(123);
print(-487.76);
print(new Date());
}
}
输出:
Apple
123
-487.76
Sun Jul 11 23:16:31 CST 2021
形参中放入数组,数组本身具有相对应的参数类型
package com.test;
public class Test {
static <T> void print(T... ts)// 泛型方法,形参是可变参数
{
for (int i = 0; i < ts.length; i++)// 访问形参数组中的每一个元素
System.out.print(ts[i] + " ");
System.out.println();
}
public static void main(String args[]) {
print("北京市", "长安街", "故宫博物院");// 3 个实际参数,类型一样
print("这台电脑", "价格", 3000.00, "元");// 4 个实际参数,类型不一样
String fruit[] = { "apple", "banana", "orange", "peach", "pear" };// String 对象数组
print(fruit);// 一个参数
}
}
输出:
北京市 长安街 故宫博物院
这台电脑 价格 3000.0 元
apple banana orange peach pear
泛型接口
package com.test;
interface Generics<T> // 泛型接口
{
public T next();
}
package com.test;
class SomethingGenerics<T> implements Generics<T>// 泛型类,实现泛型接口
{
private T something[];// 泛型域
int cursor;// 游标,标示something 中的当前元素
public SomethingGenerics(T something[])// 构造方法
{
this.something = something;
}
public T next()// 获取游标处的元素,实现接口中的方法
{
if (cursor < something.length)
return (T) something[cursor++];
return null;// 超出范围则返回空
}
}
package com.test;
public class Test {
public static void main(String args[]) {
String str[] = { "Beijing", "Shanghai", "Tianjin" };// String 对象数组,直接实例化
Generics<String> cityName = new SomethingGenerics<String>(str);// 创建泛型对象
while (true)// 遍历,将泛型对象表示的元素显示出来
{
String s = cityName.next();
if (s != null)
System.out.print(s + " ");
else
break;
}
System.out.println();
Integer num[] = { 123, -456, 789 };// Integer 对象数组,直接实例化
Generics<Integer> numGen = new SomethingGenerics<Integer>(num);// 创建泛型对象
while (true)// 遍历,将泛型对象表示的元素显示出来
{
Integer i = numGen.next();
if (i != null)
System.out.print(i + " ");
else
break;
}
System.out.println();
}
}
输出:
Beijing Shanghai Tianjin
123 -456 789
泛型参数
class LtdGenerics<T extends Number>
// 泛型类,实际类型只能是Number 的子类,Ltd=Limited
{
private T arr[]; // 域,数组
public LtdGenerics(T arr[]) // 构造方法
{
this.arr = arr;
}
public T max() // 找最大数
{
T m = arr[0];// 假设第0 个元素是最大值
for (int i = 1; i < arr.length; i++) // 逐个判断
if (m.doubleValue() < arr[i].doubleValue())
m = arr[i];// Byte、Double、Float、Integer、Long、Short 类型
// 的对象都可以调用doubleValue 方法得到对应的双精度数
return m;
}
public T min()// 找最小值
{
T m = arr[0];// 假设第0 个元素是最小值
for (int i = 1; i < arr.length; i++) // 逐个判断
if (m.doubleValue() > arr[i].doubleValue())
m = arr[i];
return m;
}
}
public class Test {
public static void main(String args[]) {
// 定义整型数的对象数组,自动装箱
Integer integer[] = { 34, 72, 340, 93, 852, 37, 827, 940, 923, 48, 287, 48, 27 };
// 定义泛型类的对象,实际类型是Integer
LtdGenerics<Integer> ltdInt = new LtdGenerics<Integer>(integer);
System.out.println("整型数最大值:" + ltdInt.max());
System.out.println("整型数最小值:" + ltdInt.min());
// 定义双精度型的对象数组,自动装箱
Double db[] = { 34.98, 23.7, 4.89, 78.723, 894.7, 29.8, 34.79, 82., 37.48, 92.374 };
// 创建泛型类的对象,实际类型是Double
LtdGenerics<Double> ltdDou = new LtdGenerics<Double>(db);
System.out.println("双精度型数最大值:" + ltdDou.max());
System.out.println("双精度型数最小值:" + ltdDou.min());
String str[] = { "apple", "banana", "pear", "peach", "orange", "watermelon" };
// 下面的语句创建泛型类的对象,这是不允许的,因为String 不是Number 类的子类
// 如果加上下面这条语句,程序不能编译通过
// LtdGenerics<String> ltdStr=new LtdGenerics<String>(str);
}
}
输出:
整型数最大值:940
整型数最小值:27
双精度型数最大值:894.7
双精度型数最小值:4.89
方法与构造方法的引用
package com.test;
public interface InterNew<String, Integer> {
Integer InterNew(String string);
}
package com.test;
public class FuncNew {
public static void main(String[] args) {
// 引用静态方法valueOf()
InterNew<String, Integer> in2 = Integer::valueOf;
// 使用valueOf()方法将字符串转换为Integer 类型,接口中的InterNew函数代表valueOf
Integer i2 = in2.InterNew("25");
System.out.println("方法引用-String -> Integer:" + i2);
}
}
输出:
方法引用-String -> Integer:25
package com.test;
public class Fruit {
private String name;
private String color;
public Fruit(String name, String color) {
super();
this.name = name;
this.color = color;
System.out.println("name = " + name + ",color = " + color);
}
public Fruit() {
super();
}
}
package com.test;
public interface FruitInter<f extends Fruit> {
// 在接口中定义抽象方法Fruit,返回的f只能是Fruit的类的子类
f Fruit(String name, String color);
}
package com.test;
public class Test {
public static void main(String args[]) {
FruitInter<Fruit> fi = Fruit::new; //获取Fruit 类构造方法的引用,接口中的Fruit相当于Fruit类构造函数
Fruit fruit = fi.Fruit("apple", "red"); //Java 编译器自动根据fi.Fruit()方法的签名,选择合适的构造函数
}
}
输出:
name = apple,color = red
Lambda作用域
利用Lambda表达式直接访问外层的局部变量
package com.test;
public class Test {
public static void main(String[] args) {
int number = 6;
//下面一行是Lambda表达式,定义了一个变量,接口中函数InterNew(5)相当于方法(t),将5+6值以字符串方式传回
InterNew<Integer, String> inn = (t) -> String.valueOf(t + number);
System.out.println("访问局部变量:" + inn.InterNew(5));
}
}
输出:
访问局部变量:11
利用Lambda表达式访问对象字段与静态变量
在 Lambda 表达式中修改变量 number (本地常量)同样是不允许的。 与本地变量不同, Lambda 表达式内部对于对象的字段以及静态变量是即可读又可写。
package com.test;
public class Test {
public static int sNum;
public int num;
public void varTest() {
InterNew<Integer, String> is = (t) -> {
num = 56; // 成员变量
return String.valueOf(t);
};
//(t)相当于接口中InterNew,返回的是Integer类的信息
System.out.println(is.InterNew(5));
InterNew<Integer, String> is2 = (t) -> {
sNum = 98; // 静态变量
return String.valueOf(t);
};
System.out.println(is2);
}
public static void main(String[] args) {
Test l = new Test();
l.varTest();
}
}
输出:
5
com.test.Test$$Lambda$2/0x0000000100000400@3abfe836