JAVA泛型及元组

泛型的作用

泛型可以让我们在使用时变换任意类型,使用泛型的话,编写一套代码就可以运用所有类型


泛型类的定义和使用

public interface Collection<E> extends Iterable<E> {}

public interface Map<K, V> {}
  • Java中这些集合就是标准的泛型类
public class ArrayList<E> {
	transient Object[] elementData;
	
	public E get(int index) {
		return (E) elementData[index];
	}
	public boolean add(E e) {
		...
		return true;
	}
}
  • 定义一个泛型类,只需在类名后面,使用尖括号将一个或多个符号包裹起来,然后在类中就可以使用该符号替换掉具体类型了,这个符号可以看作是一个参数
  • 因为可以像参数那样动态变化,所以这个符号我们称之为类型参数或者叫泛型参数
public class Generic<RudeCrab, T, E, K, V> {}
  • 类型参数可以用任意的合法标识命名,A,B,C之类的都行,我们约定俗成采用简短的大写形式,像T,E,K,V都是我们管用的标识
ArrayList<String> list = new ArrayList<>();
list.add("今天是2020-07-20,深圳天气:晴转暴雨,心情:平静");
list.add("路西法98");
list.add(98);   //编译报错
list.add(new Person());   //编译报错

for (String s: list) {
	System.out.println(s.length());
}
  • 我们来看一下使用泛型能够带来什么好处,首先,那些泛型参数的方法只能接收指定的类型,如果对象的类型不对就会编译失败,有效地避免了调用者乱传参数
  • 需要注意一点,如果我们在使用泛型类时不指定类型,那么类型参数就会将其视为Object

泛型并不只存在于Java中,它是一种编程范式,很多语言都有泛型机制,不过不同语言的泛型,背后的实现会有所不同,带来的实际效果也会有差异,Java实现的泛型机制有其特殊性和局限性


泛型方法的定义和使用

// 泛型类
class Generic<T> {
	public <E> void process(E e) {}
}

// 普通类
class Normal {
	public <E> void process(E e) {}
}
  • 泛型方法和泛型类没有必然联系,它可以定义在泛型类中也可以定义在普通类中
public class GenericMethod {
	public <T> T get<T obj> {
		//...
		return obj;
	}
}
  • 定义一个泛型方法,需要在返回值前面,使用尖括号声明一个或多个泛型参数,然后在方法中及可以用到声明的泛型参数了
GenericMethod genericMethod = new GenericMethod();
String s = genericMethod.get("路西法98");
Person p = genericMethod.get(new Person());
Integer i = genericMethod.get(98);
Double d = genericMethod.get(99.9);

调用泛型方法时,我们通常不需要像泛型类那样手动指定具体类型,因为编译器会根据你的调用,自动推导出具体类型,比如你操作的是String类型的对象,那此时参数类型就是String类型,你操作的是Person类型的对象,那此时参数类型就是Person类型,这里可以发现,参数类型只能是引用类型,不能是基本类型,就算你传入的是基本类型,编译器也会将其推导为包装类

ArrayList<Integer> list1 = new ArrayList<>();
list1.add(123);
Integer integer = list1.get(0);

ArrayList<int> list2 = new ArrayList<>(); //编译错误

我们在使用泛型类时也是如此,只能使用引用类型,不能指定基本类型,不过有自动装箱机制,在泛型类中使用基本类型时会自动装箱成包装类

上面看的是成员方法,下面我们来看静态方法

public class Generic<T> {
	private static T data;  //编译错误

	public static void setData(T data) {   //编译错误
 		this.data = data;
    }
}

泛型类有一个局限,就是静态方法和静态属性,访问不了类上定义的泛型参数

ArrayList<String> strlist = new ArrayList<>();
ArrayList<Integer> intList = new ArrayList<>();

因为我们在指定泛型参数的具体类型时,是在实例化该类时指定的,实例化,跟对象有关,静态的访问不了对象实例

public class GenericMethod {
	public static <T> T get(T t) {
		return t;
	}
}

所以,静态方法要使用泛型,就必须定义成泛型方法,和成员泛型方法一样,在返回值前面用尖括号声明泛型参数即可

泛型类泛型方法
定义在类名后面定义泛型参数在方法返回值前面定义泛型参数 可以定义在泛型类中也可以定义在普通类中
使用实例化时指定具体类型调用者推导类型
静态方法不能访问泛型参数可以访问泛型参数
使用场景泛型参数需要在多个方法或成员属性间扭转泛型参数只需作用于某个方法

总结:泛型方法和泛型类没有必然联系

//泛型类
class Generic<T> {
	public <E> void process(E e) {}
}

//普通类
class Normal {
	public <E> void process(E e) {}
}

可以在泛型类中定义泛型方法,也可以在普通类中定义

ArrayList<String> strList = new ArrayList<>();
ArrayList<Integer> intList = new ArrayList<>();

泛型类中泛型参数,需要在实例化该类时指定具体类型

GenericMethod genericMethod = new GenericMethod();
String s = genericMethod.get("今天是2020-07-21,深圳天气:晴,心情:还好")
Person p = genericMethod.get(new Person());
Integer i = genericMethod.get(98);
Double d = genericMethod.get(99.9);

而泛型方法会根据调用来进行类型推导

public class Generic<T> {
	private static T data; //编译错误

	public static void setData(T data) {  //编译错误
		this.data = data;
	}
}

静态方法访问不了泛型类的泛型参数

public class GenericMethod {
	public static <T> T get(T t) {
		return t;
	}
}

要使用泛型的话就只能将静态方法泛型化
知道了泛型类和泛型方法的特点后,也就能理解两者的使用场景

public class ArrayList<E> {
	transient Object[] elementData;

	public E get(int index) {
		return (E) elementData[index];
	}

	public boolean add(E e) {
		...
		return true;
	}
}

如果当你的泛型参数,需要在多个方法或成员属性间扭转,那就得用泛型类,比如集合这种典型的泛型类

public class GenericMethod {
	public <T> T get(T obj) {
		//...
		return obj;
	}
}

当泛型参数只需作用于某个方法,那就可以用泛型方法
一般来说,能用泛型方法就用泛型方法,因为这样不会影响到整个类

public interface Generic<T> {
	void process(T t);
}

除了泛型类和泛型方法外,泛型还可以作用在接口上,泛型接口和泛型类的定义与使用基本一致,懂了泛型类也就懂了泛型接口。



元组是什么?

  • 一种数据结构,看起来像是数组或者列表
  • 一般用”n元组“来描述,n为自然数
  • eg:(“yellow”, 5, 99.99, true)

元组的特点

  • 有序
  • 有限
  • 不可更改
  • 多类型存储
public class TupleTriple<X,Y,Z> {

    public final X x;
    public final Y y;
    public final Z z;

    public TupleTriple(X x,Y y,Z z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

class DemoProjApplicationTests {
    public static void main(String[] args) {
        String s = "luxifa";
        Integer i = 99;
        Date d = new Date();
        TupleTriple<String, Integer, Date> tt = new TupleTriple<String, Integer, Date>(s,i,d);
        System.out.println(tt);
        System.out.println(tt.x);
        System.out.println(tt.y);
        System.out.println(tt.z);
    }

输出结果:

com.luxifa.demoproj.pojo.TupleTriple@7cc355be
luxifa
99
Wed Jul 20 14:23:38 CST 2022

什么情况下使用元组?

当需要传递一组多类型数据,而这组数据本身描述了一个特定形状,又不需要更改。


举例

设计一个游戏,游戏中有一些NPC,每次游戏开始时会出现在特定的地点,特定的时间,做特定的事情,他们的属性和功能都是不变的。

(国王,宫廷花园,散步,午夜) ---------->(Human, Place, Event, Timewinow)
(哥伦布,洞穴,巡逻,白天) ---------->(Monster, Place, Event, Timewinow)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值