JDK1.5以后引入的三大常用新特性:
假设需要定义一个可以描述坐标点的类Point,需要提供两个属性x,y。对于这两个属性的内容可以有如下的选择:
在Java中,只有Object型可以保存所有的类型。
【相关代码如下】:
class Point{
private Object x;
private Object y;
public void setX(Object x) {
this.x = x;
}
public Object getX() {
return x;
}
public void setY(Object y) {
this.y = y;
}
public Object getY() {
return y;
}
}
public class test{
public static void main(String[] args) {
Point point1 = new Point();
//自动装箱并且向上转型为Object:
point1.setX(10);
point1.setY(20);
//由Object强制向下转型为int,并且自动拆箱:
int x = (Integer)point1.getX();
int y = (Integer)point1.getY();
System.out.println("整型坐标:("+x+","+y+")");
Point point2 = new Point();
//自动装箱并且向上转型为Object:
point2.setX(10.1);
point2.setY(20.1);
//由Object强制向下转型为double,并且自动拆箱:
double z = (Double)point2.getX();
double w = (Double)point2.getY();
System.out.println("浮点型坐标:("+z+","+w+")");
Point point3 = new Point();
//自动装箱并且向上转型为Object:
point3.setX("东经20度");
point3.setY("北纬40度");
//由Object强制向下转型为String,并且自动拆箱:
String a = (String)point3.getX();
String b = (String)point3.getY();
System.out.println("字符串坐标:("+a+","+b+")");
//与point4代码对比:
Point point5 = new Point();
point5.setX(10);
point5.setY("北纬40度");
System.out.println("字符串坐标:("+point5.getX()+","+point5.getY()+")");
//错误代码:在主方法中,是用户输入,但是这里用户并不知道x的
//坐标是double,接收方也不知道用户的输入,就会产生问题:
//ClassCastException(指两个没有关系的对象因为强转而出现的异常),所以说
//向下转型并不安全,会带来隐患。
Point point4 = new Point();
point4.setX(10);
point4.setY("北纬40度");
String c = (String)point4.getX();
String d = (String)point4.getY();
System.out.println("字符串坐标:("+c+","+d+")");
}
}
【注意】:
(1)泛型的定义:
(2)如何进行泛型的操作?
(3)泛型类的基本语法:
class MyClass{
T value1;
}
【注意】:
(4)泛型类的使用:
【注意】:
泛型不仅可以定义类,还可以单独来定义方法。
(1)泛型方法的定义格式:
class MyClass{
public void testMethod(T t){
System.out.println(t);
}
}
【注意】:
class MyClass{
public static T testMethod(T t){
return t;
}
}
public class Test1{
public static void main(String[] args) {
MyClass myClass = new MyClass();
System.out.println(myClass.testMethod(10));
}
}
泛型方法与泛型类可以同时存在,如下代码所示:
class MyClass {
//泛型方法:使用别的类型参数名,避免与泛型类混淆
public E printT(E e){
return e;
}
public void ptintOthers(){
//整型:
System.out.println(123);
}
}
public class test{
public static void main(String[] args) {
MyClass myClass = new MyClass();
myClass.ptintOthers();
//String类型:
System.out.println(myClass.printT("123木头人"));
}
}
【注意】:
(1)问题引入:
使用泛型类很好的避免了向下转型,使得程序变得安全,不在出现ClassCastException的问题,但是,使用泛型会带来参数不统一的问题,观察下面的代码:
class Message {
private T message;
public T getMessage(){
return message;
}
public void setMessage(T message){
this.message = message;
}
}
public class test{
public static void main(String[] args) {
Message message1 = new Message();
message1.setMessage("hello world");
fun(message1);
/**
* 注意:如果用户输入的不是String类型,就会出现新的问题
* Message message2 = new Message();
* message2.setMessage(123);
* fun(message2);//这里就会出现错误
*/
}
public static void fun(Message temp){
System.out.println(temp.getMessage());
}
}
(2)解决泛型类型带来的参数不统一的问题——使用通配符
现在需要的解决方案:可以接收所有类型的泛型,但是又不能够让用户随意修改,这时候就可以通过通配符“?”解决此问题,示例代码如下所示:
class Message {
private T message;
public T getMessage(){
return message;
}
public void setMessage(T message){
this.message = message;
}
}
public class test{
public static void main(String[] args) {
Message message1 = new Message();
message1.setMessage("hello world");
fun(message1);
Message message2 = new Message();
message2.setMessage(123);
fun(message2);
}
//注意:此时使用通配符“?”代表它可以接收任意类型,但是由于
//类型不确定,所以无法修改。
public static void fun(Message> temp){
System.out.println(temp.getMessage());
}
}
(3)在“?”的基础上又产生的两个子通配符:
? extends 类:设置泛型上限:
[例如]:
? extends Number,表示只能够设置Number或其子类,比如:Integer、Double等;
? super 类:设置泛型下限:
[例如]:
? super String,表示只能够设置String及其父类Object;
▼观察设置泛型上限:可以用在声明,不能修改
class Message{
private T message;
public T getMessage(){
return message;
}
public void setMessage(T message){
this.message = message;
}
}
public class test{
public static void main(String[] args) {
Message message = new Message();
message.setMessage(123);
fun(message);
}
//表示只能够设置Number类及其子类:
//此时使用通配符“?”表示它可以接收任何类型,但是由于不确定类型,所以无法修改
public static void fun(Message extends Number> temp){
//temp.setMessage(100);依然无法修改
System.out.println(temp.getMessage());
}
}
▼观察设置泛型下限:只能够用在方法参数中,可以修改
class Message{
private T message;
public T getMessage() {
return message;
}
public void setMessage(T message){
this.message = message;
}
}
public class test{
public static void main(String[] args) {
Message message = new Message();
message.setMessage("123木头人");
fun(message);
}
public static void fun(Message super String> temp){
//此时可以修改:
temp.setMessage("123");
System.out.println(temp.getMessage());
}
}
泛型可以定义在类中,也可以定义在接口里面,这就是所谓的泛型接口。
(1)泛型接口的定义:
Interface IMessage {
public void print(T t);
}
(2)对于这个接口,实现子类的两种做法:
【在子类定义的时候继续使用泛型】:
interface IMessage{
public void print(T t);
}
class MessageImpl implements IMessage{
@Override
public void print(T t) {
System.out.println(t);
}
}
public class test{
public static void main(String[] args) {
IMessage msg = new MessageImpl<>();
msg.print("123木头人");
}
}
【在子类实现接口的时候明确给出具体的做法:
interface IMessage{
public void print(T t);
}
class MessageImpl implements IMessage{
@Override
public void print(String t) {
System.out.println(t);
}
}
public class test{
public static void main(String[] args) {
IMessage msg = new MessageImpl();
msg.print("123木头人");
}
}
六、类型擦除
泛型是在JDK1.5之后才引进的概念,由上述可以看出,泛型代码可以与之前的版本很好的兼容,这是因为,泛型信息仅仅存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除,专业属于就叫做类型擦除,也就是说,泛型与普通的类在Java虚拟机内没有什么不同。
class MyClass{
private T message;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
public void testMethod1(T t){
System.out.println(t);
}
}
public class test{
public static void main(String[] args) {
MyClass myclass1 = new MyClass<>();
MyClass myClass2 = new MyClass<>();
//注意:getClass() class extends MyClass>设置了泛型类的上限。
System.out.println(myclass1.getClass() == myClass2.getClass());
//结果是:true,因为MyClass和MyClass在JVM中的Class
//都是MyClass.class
}
}
【观察泛型中的类型擦除】:
import java.lang.reflect.Field;
class MyClass{
private T message;
private E text;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
public E getText() {
return text;
}
public void setText(E text) {
this.text = text;
}
public void print(T t, E e){
System.out.println(t+","+e);
}
}
public class test{
public static void main(String[] args) {
MyClass myClass = new MyClass<>();
//取得类对象
Class cls = myClass.getClass();
Field[] field = cls.getDeclaredFields();
for (Field field1: field) {
System.out.println(field1.getType());
}
}
}
【注意】:
在泛型类被类型擦除的时候,之前的泛型类参数部分如果没有指定上限,如则会被转译成普通的Object类型,如果指定了上限如则类型参数就会被替换为类型上限。