什么是泛型
《Java编程思想》对 泛型的介绍:一般的类和方法,只能使用具体的类型: 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。
因此,泛型:就是适用于多种类型。从代码上讲,就是对类型实现了参数化。
语法
class 泛型类名称 < 类型形参列表 > {// 这里可以使用类型参数}class ClassName < T1 , T2 , ..., Tn > {}
例如:我们要实现一个类,类中包含一个数组成员,使得数组中可以存放任何类型的数据,也可以根据成员方法返回数组中某个下标的值
-
class MyArray2<T>{
-
public T[]
array=(T[])
new
Object[
10];
-
public T getValue(
int pos){
-
return
array[pos];
-
}
-
public
void setValue(
int pos, T val){
-
array[pos]=val;
-
}
-
-
-
}
-
public
class Test2 {
-
public
static
void main(
String[] args) {
-
//<>里面指定了类型,说明此时这个类里面只能放这个数据类型的数据
-
//并且<>中是引用类型
-
MyArray2<
String> myArray2=
new MyArray2<
String>();
-
myArray2.setValue(
0,
"hello");
-
myArray2.setValue(
1,
"world");
-
System.out.println(myArray2.getValue(
0));
-
System.out.println(myArray2.getValue(
1));
-
MyArray2<
Integer> myArray3=
new MyArray2<
Integer>();
-
myArray3.setValue(
2,
100);
-
System.out.println(myArray3.getValue(
2));
-
}
-
}
1.<>当中指定类型后,编译器会根据你指定的类型参数来进行类型的检查
2.取元素时不用进行强转
T[]array=new T[10]//error是错误的
泛型的使用
语法
泛型类 < 类型实参 > 变量名 ; // 定义一个泛型类引用new 泛型类 < 类型实参 > ( 构造方法实参 ); // 实例化一个泛型类对象
例如上面代码:MyArray2<String> myArray2=new MyArray2<Stirng>();//后面<>中的类型可写可不写,编译器可以根据上下文推导出类型实参。
泛型如何编译
擦除机制
我们通过字节码发现在编译时,所有的T都变成了Object.
所以擦除机制就是在 编译的时候 把泛型T 擦除成了Object.运行期间没有这个泛型这个概念。
思考:既然T都被擦除成了Object了,为什么不能实例化泛型类型的数组?
T[]array=new T[10]//等价于Object[]array=new Object[10]; 错误
-
class MyArray2<T>{
-
public T[]
array=(T[])
new
Object[
10];
-
public T getValue(
int pos){
-
return
array[pos];
-
}
-
public
void setValue(
int pos, T val){
-
array[pos]=val;
-
}
-
public T[] getArray(){
-
return
array;
-
}
-
}
-
public
class Test2 {
-
public
static
void main(
String[] args) {
-
MyArray2 myArray2=
new MyArray2();
-
myArray2.setValue(
0,
10);
-
myArray2.setValue(
1,
"hello");
-
Integer[] integers= (
Integer[]) myArray2.getArray();
-
System.out.println(Arrays.toString(integers));
-
}
-
}
因为返回的Object数组里面,可能存放的是任何的数据类型,可能是String,可能是Person,运行的时候,直接转给Intefer类型的数组,编译器认为是不安全的。
正确的创建方法(了解即可)
-
class MyArray3<T>{
-
T[]
array;
-
/**
-
* 通过放射创建,指定类型的数组
-
* @param clazz
-
* @param capacity
-
*/
-
public MyArray3(
Class<T>clazz,int capacity){
-
array=(T[])
Array.newInstance(clazz,capacity);
-
}
-
public T getPos(
int pos){
-
return
array[pos];
-
}
-
public
void setArray(
int pos,T val){
-
array[pos]=val;
-
}
-
public T[] getArray(){
-
return
array;
-
}
-
}
-
public
class Test3 {
-
public
static
void main(
String[] args) {
-
MyArray3<
Integer> myArray3=
new MyArray3<>(
Integer.
class,
10);
-
myArray3.setArray(
0,
10);
-
myArray3.setArray(
1,
15);
-
Integer[] integers=myArray3.getArray();
-
System.out.println(Arrays.toString(integers));
-
}
-
}
泛型的边界
有上边界,无下边界。
语法
class 泛型类名称 < 类型形参 extends 类型边界 > {...}
例如:
public class MyArray < E extends Number > {...}
这里的extends不是继承的意思,表示边界是Number及其子类
应用:实现一个泛型类,找出数组最大值
因为泛型类T不是基本类型,不能简单的进行大于小于这样的比较,T是引用类型要使用比较器,也就是将T擦除成Comparable接口。
-
class Alg<T extends Comparable<T>>{
//将T擦成Comparable接口
-
public
T findMax(
T[] array){
-
T max=array[
0];
-
for (int i =
0; i < array.length; i++) {
-
if(max.compareTo(array[i])<
0){
-
max=array[i];
-
}
-
}
-
return max;
-
}
-
}
泛型方法
1.成员方法:方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { ... }
-
class Alg{
-
public<T
extends Comparable<T>> T findMax(T[]
array){
-
T max=
array[
0];
-
for (
int i =
0; i <
array.length; i++) {
-
if(max.compareTo(
array[i])<
0){
-
max=
array[i];
-
}
-
}
-
return max;
-
}
-
}
-
public
class Test {
-
public
static
void main(
String[] args) {
-
Alg alg=
new Alg();
-
Integer[]
array=
new
Integer[]{
1,
23,
4,
56,
7,
8};
-
System.out.println(alg.findMax(
array));
-
}
-
}
56
2.静态方法,静态的泛型方法 需要在static后用<>声明泛型类型参数
-
class Alg2{
-
//静态方法不依赖于对象的调用
-
public
static<T
extends Comparable<T>> T findMax(T[]
array){
-
T max=
array[
0];
-
for (
int i =
0; i <
array.length; i++) {
-
if(max.compareTo(
array[i])<
0){
-
max=
array[i];
-
}
-
}
-
return max;
-
}
-
}
通配符
-
class
Message<T>{
-
private T message;
-
-
public T
getMessage(
) {
-
return message;
-
}
-
-
public
void
setMessage(
T message) {
-
this.
message = message;
-
}
-
}
-
public
class
Test2 {
-
public
static
void
func(
Message<?> message){
-
//message.setMessage(100);//不能修改,因为不知道参数是什么类型的
-
System.
out.
println(message.
getMessage());
-
}
-
public
static
void
main(
String[] args) {
-
Message<
String> message=
new
Message<>();
-
String s=
"中国世界第一";
-
message.
setMessage(s);
-
func(message);
-
Message<
Integer> message2=
new
Message<>();
-
Integer a=
10;
-
message2.
setMessage(a);
-
func(message2);
-
}
-
}
通过使用通配符,既可以传String也可以传Integer,扩大了参数的范围
通配符的上界(常用于获取元素)
语法
<? extends 上界 ><? extends Number > // 可以传入的实参类型是 Number 或者 Number 的子类
-
class Food{
-
-
}
-
class Fruit extends Food{
-
-
}
-
class Apple extends Fruit{
-
-
}
-
class Banana extends Fruit{
-
-
}
-
class Alg2<T>{
-
private
T
val;
-
-
public
T getVal() {
-
return
val;
-
}
-
-
public void setVal(
T
val) {
-
this.
val =
val;
-
}
-
}
-
public
class Test3 {
-
/**
-
* ?通配符extends上限是Fruit及其子类
-
* @param temp
-
*/
-
public static void func(
Alg2<?
extends
Fruit> temp){
-
/*
-
Banana banana=temp.getVal();
-
Apple apple=temp.getVal();
-
不能进行修改,因为temp接收的是Fruit及其子类,无法确定参数是什么类型
-
*/
-
Fruit fruit= temp.getVal();
//temp放的是Fruit及其子类,所以可以用Fruit进行接收
-
System.out.println(temp.getVal());
-
}
-
public static void main(
String[] args) {
-
Alg2<
Banana> alg2=
new
Alg2<>();
-
alg2.setVal(
new
Banana());
-
func(alg2);
-
Alg2<
Apple> alg3=
new
Alg2<>();
-
alg3.setVal(
new
Apple());
-
func(alg3);
-
}
-
}
通配符的下限(常用于设置元素)
语法
<? super 下界 ><? super Integer > // 代表 可以传入的实参的类型是 Integer 或者 Integer 的父类类型
应用:
-
class Food{
-
-
}
-
class Fruit extends Food {
-
-
}
-
class Apple extends Fruit {
-
-
}
-
class Banana extends Fruit {
-
-
}
-
class Alg4<T>{
-
private
T
val;
-
-
public
T getVal() {
-
return
val;
-
}
-
-
public void setVal(
T
val) {
-
this.
val =
val;
-
}
-
}
-
public
class Test4 {
-
/**
-
* 通配符?下限super是Fruit及其父类
-
* @param temp
-
*/
-
public static void func(
Alg4<?
super
Fruit> temp){
-
/**
-
* 因为最小的下限是Fruit,所以可以使用setVal进行修改
-
*/
-
temp.setVal(
new
Banana());
//向上转型
-
temp.setVal(
new
Apple());
//向上转型
-
/**
-
* temp可能是Fruit或者他的父类,无法用子类(Fruit)接收
-
*/
-
//Fruit fruit=temp.getVal();
-
-
System.out.println(temp.getVal());
-
}
-
public static void main(
String[] args) {
-
Alg4<
Fruit> alg4=
new
Alg4<>();
-
alg4.setVal(
new
Fruit());
-
Alg4<
Food> alg41=
new
Alg4<>();
-
alg41.setVal(
new
Food());
-
}
-
}
(自动)装箱
-
public
static
void main(
String[] args) {
-
int a=
10;
-
Integer integer1=a;
//自动装箱
-
Integer integer2=
new
Integer(a);
//显示装箱
-
Integer integer3=
Integer.valueOf(a);
//显示装箱
-
}
通过字节码我们发现,装箱的底层是采用Integer.valueOf
(自动)拆箱
-
public
static
void main(
String[] args) {
-
Integer
integer=
10;
-
int val=
integer;
//自动拆箱
-
System.out.println(val);
-
int val2=
integer.intValue();
//显示拆箱
-
double val3=
integer.doubleValue();
//显示拆箱
-
}
面试题:输出什么
-
public
static
void main(
String[] args) {
-
Integer a=
127;
-
Integer b=
127;
-
System.out.println(a==b);
-
Integer c=
128;
-
Integer d=
128;
-
System.out.println(c==d);
-
}
答案:
true
false
why?
装箱的底层是valueOf,查看他的源码发现在【-127,128】范围内有一个数组,不在这个范围内需要new 对象,所以127在数组内相同,128需要new 对象,地址不同
转载:https://blog.csdn.net/weixin_61427900/article/details/125505139