一:泛型:
如何正确使用Java泛型 - JJian - 博客园 (cnblogs.com) <--点击可以查看别人写的泛型文章
java中强制类型转换 - 易小川 - 博客园 (cnblogs.com) <--强制类型转换
(一):集合中也可以放类的实例化对象。任何一种数据类型或者对象放进容器中后都会失去原有的类型,变成 Object,用的时候从容器中取出后进行转型成所需的类型就可以了,
在Java中由于继承和向上转型,子类可以非常自然地转换成父类,但是父类转换成子类则需要强制转换。
举个例子来说明。比如系统中存在Father、Son两个对象。首先我们先构造一个Son对象,然后用一个Father类型变量引用它:
Father father = new Son();
在这里Son 对象实例被向上转型为father了,但是请注意这个Son对象实例在内存中的本质还是Son类型的,只不过它的能力临时被消弱了而已,如果我们想变强怎么办?将其对象类型还原!
Son son = (Son)father;
这条语句是可行的,其实father引用仍然是Father类型的,只不过是将它的能力加强了,将其加强后转交给son引用了,Son对象实例在son的变量的引用下,恢复真身,可以使用全部功能了。
前面提到父类强制转换成子类并不是总是成功,那么在什么情况下它会失效呢?
当引用类型的真实身份是父类本身的类型时,强制类型转换就会产生错误。例如:
Father father = new Father();
Son son = (Son) father;
这个系统会抛出ClassCastException异常信息。
简单说的话就是能力加强和减弱。Son对象能力先被减弱,之后再加强,交给了Son来引用。
以下的代码在编译时不报错,运行时错误。原因在于上面的第1点,从集合中取出时,默认是object类型,要进行类型转换。主要是第二个coin类型不合。
public class RawType_Class {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add(new Stamp());
list.add(new Coin());
for (Iterator i = list.iterator(); i.hasNext();) {
Stamp stamp = (Stamp) i.next();
}
}
}
我又自己简单试了试:
package practice1;
public class ClassChange {
public static void main(String[] args) {
animal a = new doge();//没继承关系就根本不能转。继承之后多态。a的本质是doge,被animal暂时减弱
animal b = new catt();//b在内存中的本质还是catt(),能力暂时被减弱,要想回复,按照以下行恢复。
doge ok = new doge();
catt c = (catt)b;//编译运行都正确
catt d = (catt)a;//运行出错java.lang.ClassCastException,引用子类的真正类型才能成功。不是这个真正类型转化回来就要出错。
shechu pp = new catt();//没有继承关系编译阶段就错误。
}
}
class doge extends animal{}
class catt extends animal{}
class animal{}
class people{}
class shechu extends people{}
Exception in thread "main" java.lang.ClassCastException: practice1.doge cannot be cast to practice1.catt
at practice1.ClassChange.main(ClassChange.java:9)
所以编译器在编译时只会检查类型之间是否存在继承关系,有则通过;而在运行时就会检查它的真实类型,是则通过,否则抛出
ClassCastException异常。
所以在继承中,子类可以自动转型为父类,但是父类强制转换为子类时只有当引用类型真正的身份为子类时才会强制转换成功,否则失败
(二)泛型: 泛型的初衷(减少强制类型转换以及确保类型安全)
针对利用继承来实现通用程序设计所产生的问题,泛型提供了更好的解决方案:类型参数。例如,ArrayList类用一个类型参数来指出元素的类型。
ArrayList<String> stringValues=new ArrayList<String>();
泛型类型变量不能是基本数据类型
就比如,没有ArrayList<double>,只有ArrayList<Double>。因为当类型擦除后,ArrayList的原始类中的类型变量(T)替换为Object,但Object类型不能存储double值。
二:以下是一段我自己写的测试,写的不好请见谅。这里我定义了一个泛型类,就相当于说明了这个类中可以通用<T>这个泛型类型了。
package myTry;
public class FanXin1 {
public static void main(String[] args) {
Array arr = new Array();//对的
arr.setT(100);
Array arr2 = new Array();
arr2.setT("哈哈哈哈");
System.out.println(arr.getT());
System.out.println(arr2.getT());
//之前的这两个看似没有问题,那换成数组呢?
Array[] arr3 = new Array[3];
arr3[0] = new Array("wovvvcasi");
arr3[1] = new Array(124);
for(int i = 0;i<2;i++) {
System.out.println(arr3[i].getT());
}
//接下来指定类型
Array<Integer> arr4 = new Array<>();
arr4.setT(1);//只能放数字,其他类型会出错。
}
}
//定义泛型
class Array<T>{//看示例里面咋用。
private T t;
public Array() {}
public Array(T t) {
this.t = t;}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
100
哈哈哈哈
wovvvcasi
124
泛型方法:
<T>说明是泛型类型,T是返回值类型,参数里面的T就是参数类型了。
package myTry;
public class FanXin2 {
public static void main(String[] args) {
Array2 ar2 = new Array2();
System.out.println(ar2.show("sdfsdf"));
System.out.println(ar2.show(true));
System.out.println(ar2.show(12345));
}
}
class Array2{
public <T> T show(T t) {//中间的T就是返回类型。<T>说明是泛型类型,说明统一是这个格式。
return t;
}
}
sdfsdf
true
12345
泛型方法和泛型接口或泛型类:
定义泛型方法,同样要有<T>说明使用了泛型,其余的和之前同理。在泛型接口下或者泛型类下,可以用泛型定义变量等等。可以用多个泛型作为参数(不知道咋表达出来,可以看下面的代码)。
package myTry;
public class FanXin2 {
public static void main(String[] args) {
Array2 ar2 = new Array2();
System.out.println(ar2.show("sdfsdf"));
System.out.println(ar2.show(true));
System.out.println(ar2.show(12345));
}
}
class Array2 {
public <T> T show(T t) {// 中间的T就是返回类型。<T>说明是泛型类型,说明统一是这个格式。
return t;
}
}
interface Array3<T> {// 这里放<T>则说明使用泛型,之后的类型自己跟着变。
T show(T t);//泛型T在这里是泛型接口。
<M> T opps(T t,M m);//定义泛型方法
}
class Array3imple<T> implements Array3<T> {// 这里用T是因为实现类要实现抽象类,需要泛型类型。
public T show(T t) {
return t;
}
@Override
public <M> T opps(T t, M m) {
// TODO Auto-generated method stub
return null;
}
}
class Array4<T> implements Array3<T>{
@Override
public T show(T t) {
// TODO Auto-generated method stub
return null;
}
@Override
public <M> T opps(T t, M m) {
// TODO Auto-generated method stub
return null;
}
}
泛型方法:
不确定方法参数类型时
public <T> void test(T t){
System.out.println(t.getClass().getName);
}
调用时可以传入任意类型的参数,根据传入参数类型决定参数类型
(三)类型通配符:
通配符的上限和下限:
以下是我自己写的一段测试,写的不好请见谅。(针对通配符上限和下限)
看代码时记得看清楚子类父类分别是哪些!
List是泛型类,建议使用这个进行测试
package myTry;
import java.util.ArrayList;
import java.util.List;
public class TongPeiFu {
public static void main(String[] args) {
// MaykiParent may = new MaykiParent();
// Student stu = new Student();
// MaykiParent stu2 = new Student();
List<MaykiParent> maykiParent = new ArrayList();
ArrayList<Student> student = new ArrayList();
ArrayList<Son> son = new ArrayList();
//先来测试extends(下限),MaykiParent是Student的父类。
print(maykiParent);
print(student);
print(son);//下限能接受自己和子类,所以不会报错
ArrayList<Integer> integer = new ArrayList();
// print(integer);//报错
input(maykiParent);
// input(son);//上限的就不能接受子类了。所以这里报错。但上限能接受自己和父类
}
public static void print(List<? extends MaykiParent> list) {//这里前提应该是泛型类
System.out.println("第一种");
}
public static void input(List<? super Student> list) {
System.out.println("第二种");
}
}
class MaykiParent{
// public void print(MaykiParent<? extends MaykiParent> x) //这里用这个测试是错的,推荐用List集合类测试。
}
class Student extends MaykiParent{
}
class Son extends Student{
}
第一种
第一种
第一种
第二种
(这张图截取自b站)
简单来说,比如是super MayiktParent,就是它本身或者它的父类都能被这个泛型方法接受。extends MayiktParent,就是它本身和它的子类都可以被这个定义的泛型方法接受。
(四):可变参数:
以下是我自己写的一段测试:
package myTry;
public class KeBianCanShu {
//可变参数:参数动态可变(我觉得这个了解即可)
//使用可变参数前:(这样的话,即使要重载,也要在以后不断改变函数的参数)
public static int sum(int a,int b) {
return a+b;
}
public static int sum(int a,int b,int c) {
return a+b+c;
}
//使用可变参数后:
public static int sum2(int... a) {//格式就是括号中显示的那样。
int result = 0;
for(int i = 0;i<a.length;i++) {
result += a[i];//就相当于了一个数组,你传入多少参数,数组就有多少个值。
}
return result;
}
public static void main(String[] args) {
System.out.println(sum2(1,1,1,1,1));
System.out.println(sum(1,1));
}
}
(五):泛型的擦除(不太懂,简单百度了一下):
泛型,是在编译阶段限制传递的类型。
转化成 .class文件过后,就在文件中擦除了泛型,下图经过反编译发现了其实是Object类型,不是泛型类型。
(图片来自b站)