Java基础之类型信息(一)

我们都知道对于面向对象的语言,一般都会在编译期执行类型检查。那如果我们要在运行期间如何对数据类型进行提取并识别那,在Java里面有两种方式可以做到这一点。其一,利用传统的RTTI,它假定我们在编译时就已经知道所有类型。另一种是“反射”机制,它允许我们在运行时发现和使用类的信息。

RTTI(运行时类型识别)

RTTI的一般形式

我们先来看一段代码

abstract class Shape {
  void draw() { System.out.println(this + ".draw()"); }
  abstract public String toString();
}

class Circle extends Shape {
  public String toString() { return "Circle"; }
}

class Square extends Shape {
  public String toString() { return "Square"; }
}

class Triangle extends Shape {
  public String toString() { return "Triangle"; }
}	

public class Shapes {
  public static void main(String[] args) {
    List<Shape> shapeList = Arrays.asList(
      new Circle(), new Square(), new Triangle()
    );
    for(Shape shape : shapeList)
      shape.draw();
  }
}

在这个例子中,当Shape对象存入到List<Shape>的数组时会向上转型。但是向上转型为Shape的时候会丢失具体类型。对于数组来说,它们都是Shape类型,这里要特殊说明一下,当数组的元素被取出的过程中实际的过程是它会把所有的事物当做Object持有,之后会发生结果转型,转回Shape的过程。这也正是RTTI最经典的使用方式

同时,也说明一点,在这里RTTI类型转换并没有彻底:Object被转型为Shape,而不是转型为Circle、Square或者Triangle。之后Shape具体执行哪种代码,就交给多态机制操作

这也是我们代码编写过程中惯用的方法,尽可能少的了解对象的具体类型,只与一个通用的窗口打交道。这种写法也便于理解项目同时,也更容易维护。

RTTI实现机制

RTTI功能的实现是由Class对象来完成的,它包含了类的所有相关信息,而这个对象是在编写并编译一个新的Java类时产生的。而且这个对象只有在其使用时才会被加载。那我们该如何获取这个Class对象那,其实他和普通对象一样,通过以下三种方式来获取,

方式1:通过Object类的getObject()方法 

Person p = new Person();
Class c = p.getClass();

方式2: 通过 类名.class 获取到字节码文件对象(任意数据类型都具备一个class静态属性,看上去要比第一种方式简单)

Class c2 = Person.class;

方式3: 通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)

Class c3 = Class.forName("Person");

这三种方式中,一般会选择第三种方式,因为在你获取恰当的Class对象的引用时,不需要像前两种方式需要拥有该类型对象

当你获取到Class对象之后,你就可以使用它来获取你想要的了解的类型的所有信息,包括创建对象。

Class是如何创建对象的,大家先看以下代码:

import static net.mindview.util.Print.*;

interface HasBatteries {}
interface Waterproof {}
interface Shoots {}

class Toy {
  // Comment out the following default constructor
  // to see NoSuchMethodError from (*1*)
  Toy() {}
  Toy(int i) {}
}

class FancyToy extends Toy
implements HasBatteries, Waterproof, Shoots {
  FancyToy() { super(1); }
}

public class ToyTest {
  static void printInfo(Class cc) {
    print("Class name: " + cc.getName() +
      " is interface? [" + cc.isInterface() + "]");
    print("Simple name: " + cc.getSimpleName());
    print("Canonical name : " + cc.getCanonicalName());
  }
  public static void main(String[] args) {
    Class c = null;
    try {
      c = Class.forName("typeinfo.toys.FancyToy");
    } catch(ClassNotFoundException e) {
      print("Can't find FancyToy");
      System.exit(1);
    }
    printInfo(c);	
    for(Class face : c.getInterfaces())
      printInfo(face);
    Class up = c.getSuperclass();
    Object obj = null;
    try {
      // Requires default constructor:
      obj = up.newInstance();
    } catch(InstantiationException e) {
      print("Cannot instantiate");
      System.exit(1);
    } catch(IllegalAccessException e) {
      print("Cannot access");
      System.exit(1);
    }
    printInfo(obj.getClass());
  }
}

可以通过newInstance()方法来创建对象,该方法是实现“虚拟构造器”的一种途径,虚拟构造器允许你声明:“我不知道你的确切类型,但是无论如何要正确地创建”。在这段代码中,up仅仅只是一个Class引用,但这个引用通过该方法可以成功的指向Toy对象。如果在实际生活中,应用这个方法,你需要很清楚的知道这个Class对象到底是什么,并能够执行什么转型。另外,在对象正确创建的过程中,必须是使用的默认构造器,不能使用其他构造器。这是该方法出现的缺点。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值