目录
1. 什么是泛型
2. 泛型的引入
3.泛型
3.1 泛型的语法
3.2 泛型的引用
3.3 泛型的上界?
4. 泛型方法
?5. 通配符
?5.1通配符的上界
5.2 通配符的下界
4. 拓展
1. 什么是泛型
一般的类和方法,只能使用具体的类型
:
要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的 代码,这种刻板的限制对代码的束缚就会很大。-----
来源《
Java
编程思想》对泛型的介绍。
泛型是在
JDK1.5
引入的新的语法,通俗讲,泛型:
就是适用于许多许多类型
。从代码上讲,就是对类型实现了参数化。
2. 泛型的引入
我们在之前的数组的学习中,我们知道,数组的类型我们给定之后,我们在数组中存放的数据他的类型就是我们所给的数组的类型
?我们没有办法去存放其他类型的数据元素,如果去添加其他类型的数据类型,就会报错.
?发现,此时就会报错,那么怎么来存放这样的一个数组呢?我们知道,在我们的所有的数据类型他们都统一来自于我们的object这个父类.那么我们是不是可以定义一个object数组,来存放不同的数据类型的元素呢?
此时我们就发现可以来定义这样的数组来存放不同类型的元素.
那么我们在在这给大家一段代码,让我们来存储数组的元素和打印数组的元素,
class MyArray{
Object [] obj = new Object[10];//定义Object数组
public void setObj(int sot,int val) {//在sot下标下存入val
this.obj[sot] = val;
}
public Object getObj(int sot) {//访问sot下标的元素
return this.obj[sot];
}
}
public class test {
public static void main(String[] args) {
MyArray myArray = new MyArray();
myArray.setObj(0,10);
myArray.setObj(1,20);
int ret = myArray.getObj(1);
System.out.println(ret);
}
}
?当我们写出这样的代码,我们定义一个Object数组,在0下标存10,在1下标存20,当我们调用get方法的时候,我们发现,此时的代码会报错,那么为什么会报错,明明我1下标下存放的就是int型的数据,我用int去接收不应该有错误呀,但是记得我们在定义get方法的时候,我们的返回类型是Object,所以,此时要想我们的代码没有错误就必须要强转为int?
int ret = (int) myArray.getobj(1);
虽然在这种情况下,当前数组任何数据都可以存放,但是,更多情况下,我们还是希望他只能够持有一种数据类 型。而不是同时持有这么多类型。所以,泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译
器去做检查。
此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型。
那么此时就有了我们泛型.
3.泛型
3.1 泛型的语法
class 泛型类名称<类型形参列表> {
// 这里可以使用类型参数
}
class ClassName<T1, T2, ..., Tn> {
}
3.2 泛型的引用
那么我们就可以去修改上面的代码.
class MyArray<T>{
public T[] obj =(T[]) new Object[10];//定义Object数组
public void setObj(int sot,T val) {//在sot下标下存入val
this.obj[sot] = val;
}
public T getObj(int sot) {//访问sot下标的元素
return this.obj[sot];
}
}
public class test {
public static void main(String[] args) {
MyArray<Integer> myArray = new MyArray<>();
myArray.setObj(0,10);
myArray.setObj(1,20);
Integer ret = myArray.getObj(0);
System.out.println(ret);
MyArray<String> myArray1 = new MyArray<>();
myArray1.setObj(0,"hello");
myArray1.setObj(1,"hahaha");
String str = myArray1.getObj(0);
System.out.println(str);
}
}
?
此时发现不需要强转类型,这就是我们泛型的好处.
3.3 泛型的上界?
首先提出一个例子,在泛型类中有一个数组,让你去给这个数组按照从小到大的顺序去排列
class A <T> {
public T findMax(T[] array){
T max = array[0];//假设0下标的是最大值
for (int i = 1; i < array.length ; i++) {
if (max < array[i]){
max = array[i];
}
}
return max;
}
}
public class Test2 {
public static void main(String[] args) {
A<Integer> a = new A<>();
Integer [] array = {1,2,3,4};
Integer ret = a.findMax(array);
System.out.println(ret);
}
}
当我写完这样的代码的时候,我发现我都是按照题目的要求去求解的,但是此时在判断的时候JVM报错了,那么为什么会报错,因为在我们的JVM中,我们不知道传入的参数类型是什么,不同的类型你怎么让JVM去给你比较.那么此时相信大多数人都想到了我们的compareTo,用它来实现,那么怎么去使用呢.
class A <T extends Comparable<T>> {
public T findMax(T[] array){
T max = array[0];//假设0下标的是最大值
for (int i = 1; i < array.length ; i++) {
if (max.compareTo(array[i]) < 0){
max = array[i];
}
}
return max;
}
}
?
?此时我们寻找最大的值就找到了.
class A <T extends Comparable<T>> {
public T findMax(T[] array){
T max = array[0];//假设0下标的是最大值
for (int i = 1; i < array.length ; i++) {
if (max.compareTo(array[i]) < 0){
max = array[i];
}
}
return max;
}
}
class Preson implements Comparable<Preson> {
public int age;
public Preson(int age){
this.age =age;
}
@Override
public String toString() {
return "Preson{" +
"age=" + age +
'}';
}
@Override
public int compareTo(Preson o) {
return this.age - o.age;
}
}
public class Test2 {
public static void main(String[] args) {
A<Preson> a = new A<>();
Preson[] preson ={new Preson(10),new Preson(20)};
Preson ps = a.findMax(preson);
System.out.println(ps);
}
?在这我们根据一个Person数组,通过年龄的大小来输出了年龄最大的.在这我们的person类继承了我们的Conparable接口,那么我们就可以在泛型类中传入我们的Person这种类型,此时我们的Person就是我们此时的泛型类的上界,也就是说A<类类型>,我们放进去的类类型就是我们的泛型类的上界.
泛型上界的语法方式:
class
泛型类名称
<
类型形参
extends
类型边界
>
{
...
}
?
注意: 泛型没有下界.
4. 泛型方法
就是八把一个方法给他写成返回值是泛型的一个方法:
泛型方法的语法:
方法限定符
<
类型形参列表
>
返回值类型
方法名称
(
形参列表
) { ... }
在这给大家举一个例子:
class Preson implements Comparable<Preson> {
public int age;
public Preson(int age){
this.age =age;
}
@Override
public String toString() {
return "Preson{" +
"age=" + age +
'}';
}
@Override
public int compareTo(Preson o) {
return this.age - o.age;
}
}
class A2 {
public static <T extends Comparable>T findMax(T[] array){
T max = array[0];//假设0下标的是最大值
for (int i = 1; i < array.length ; i++) {
if (max.compareTo(array[i]) < 0){
max = array[i];
}
}
return max;
}
}
public class Test2 {
public static void main(String[] args) {
Preson[] presons = {new Preson(10),new Preson(20)};
Preson ret = A2.findMax(presons);
System.out.println(ret);
}
在这,我是将我们的泛型方法直接转换成了静态方法.那么此时有人要问了那你传参的数据类型在哪呢,我只是看到你返回的类型是Person,在这做个解释
?5. 通配符
通配符是用来解决泛型无法协变的问题的
,协变指的就是如果
Student
是
Person
的子类,那么
List<Student>
也应 该是 List<Person>
的子类。但是泛型是不支持这样的父子类关系的。
泛型
T
是确定的类型,一旦你传了我就定下来了,而通配符则更为灵活或者说是不确定,更多的是用于扩充 参数的范围.
在这举个例子说明:
class Message<T> {
private T message;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
}
public class Test3 {
public static void main(String[] args) {
Message<String> message = new Message<>();
message.setMessage("你好呀,朋友.");
func(message);
}
public static void func(Message <String> ret){
System.out.println(ret.getMessage());
}
}
这样的代码是没有错误的,但是我们的message只能传String类型的,
?
那么我们怎么让它改正确呢?
方法一:
?
所以说这样的方法是不是很麻烦,一点都不实用,一旦我message的类型一变,你就要给我的func函数的接收类型也要变.
方法2:就是使用我们的通配符
public static void main(String[] args) {
Message<String> message = new Message<>();
message.setMessage("你好呀朋友");
func(message);
}
public static void func(Message <?> ret){
System.out.println(ret.getMessage());
}
?
?
?5.1通配符的上界
通配符上界的语法:
<?
extends
上界
>
<?
extends
Number
>
//
可以传入的实参类型是
Number
或者
Number
的子类
举例说明:
class Food {
}
class Fruit extends Food {
}
class Apple extends Fruit {
}
class Banana extends Fruit {
}
class Message1<T>{
private T message;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
}
public class Test4 {
public static void main(String[] args) {
Message<Food> message = new Message<>();
message.setMessage(new Food());
func(message);
Message<Apple> message1 = new Message<>();
message1.setMessage(new Apple());
func(message1);
}
public static void func(Message<? extends Food> ret){
System.out.println(ret.getMessage());
}
}
5.2 通配符的下界
语法:
<?
super
下界
>
<?
super
Integer
>
//
代表 可以传入的实参的类型是
Integer
或者
Integer
的父类类型
?也是举个例子说明:
class Food1 {
}
class Fruit1 extends Food {
}
class Apple1 extends Fruit1 {
}
class Message2<T>{
private T message;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
}
public class Test5 {
public static void main(String[] args) {
Message2<Fruit1> message2 = new Message2<>();
message2.setMessage(new Fruit1());
func2(message2);
Message2<Food1> message21 = new Message2<>();
message21.setMessage(new Food1());
func2(message2);
}
public static void func2(Message2<? super Fruit1> ret){
ret.setMessage(new Apple1());
ret.setMessage(new Fruit1());
System.out.println(ret.getMessage());
}
}
4. 拓展
public class Test6 {
public static void main(String[] args) {
int a = 100;
int b = 100;
System.out.println(a==b);
Integer c = 200;
Integer d = 200;
System.out.println(c == d);
}
}
运行结果:
?为什么一个是ture 一个是false?
这个问题留给兄弟们,可以去参考Integer的源码,一看便知.
|