Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(十四)之Type Information
Runtime type information (RTTI) allow you to discover and use type information while a program is running
This take two forms:
1. "traditional" RTTI, which assumes that you have all the types available at compile time,
2. the reflection mechanism, which allows you to discover and use class information solely at run time
The need for RTTI
You fetch an element out of the array, the container--which is actually holding everything as an Object-automatically casts the result back to a Shape. This is the most basic form of RTTI,because all casts are checked at run time for correctness. That's what RTTI means: At run time, the type of an object is identified
But what if you have a special programming problem that’s easiest to solve if you know the exact type of a generic reference?
The Class object
the Class object contains information about the class.
each time you write and compile a new class, a single Class object is also created ( and stored, appropriately enough, in an identically named .class file). To make an object of that class, the Java Virtual Machine (JVM) that's executing your program uses a subsystem called a class loader.
The class loader subsystem can actually comprise a chain of class loaders, but there's only one primordial class loader, which is part of the JVM implementation.
The primordial class loader loads so-called trusted classes, including Java API classes, typically from the local disk
All classes are loaded into the JVM dynamically, upon the first use of a class. This happens when the program makes the first reference to a static member of that class ( the constructor is also a static method of a class)
Class.forName("Gum"); // return a Class reference, the call to forName() is being made for its side effect,
// which is to load the class Gum if it isn't already loaded
All Class objects belong to the class Class.
object.getClass()
Notice that you must use the full qualified name (including the package name) in the string that you pass to forName()
getName() //返回的是虚拟机里面的class的表示
getSimpleName()
getCanonicalName() //返回的是更容易理解的表示
isInterface()
getInterfaces()
getSuperclass()
newInstance() //the class that’s being created with newlnstance( ) must have a default constructor.
getName()和getCanonicalName() 对于普通类来说,二者没什么区别,只是对于特殊的类型上有点表示差异
比如byte[]类型,前者就是[B,后者就是byte[]
比如byte[][]类型,前者就是[[B,后者就是byte[][]
Class literals
FancyToy.class: return a reference to the Class object, safe than Class.forName(), and more efficient.
Class literals work with regular classes as well as interfaces, arrays, and primitive types.
there's a standard field called TYPE that exists for each of the primitive wrapper classes. The TYPE field produces a reference to the Class object for the associated primitive type
for example: boolean.class == Boolean.TYPE
boolean.class != Boolean.class
creating a reference to a Class object using ".class" doesn’t automatically initialize the Class object.
There are actually three steps in preparing a class for use:
1. Loading. Finds the bytecodes and creates a Class object from those bytecodes.
2. Linking. verifies the bytecodes, allocates storage for static fields, and if necessary, resolves all references to other classes made by this class
3. Initialization. If there's a supperclass, initialize that. Execute static initializers and static initialization blocks
Initialization is delayed until the first reference to a static method (the constructor is implicitly static) or to a non-constant static field
class Initable {
static final int staticFinal = 47;
static final int staticFinal2 =
ClassInitialization.rand.nextInt(1000);
static {
System.out.println("Initializing Initable");
}
}
class Initable2 {
static int staticNonFinal = 147;
static {
System.out.println("Initializing Initable2");
}
}
class Initable3 {
static int staticNonFinal = 74;
static {
System.out.println("Initializing Initable3");
}
}
public class ClassInitialization {
public static Random rand = new Random(47);
public static void main(String[] args) throws Exception {
Class initable = Initable.class;
System.out.println("After creating Initable ref");
// Does not trigger initialization:
System.out.println(Initable.staticFinal);
// Does trigger initialization:
System.out.println(Initable.staticFinal2);
// Does trigger initialization:
System.out.println(Initable2.staticNonFinal);
Class initable3 = Class.forName("Initable3");
System.out.println("After creating Initable3 ref");
System.out.println(Initable3.staticNonFinal);
}
} /* Output:
After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74
*///:~
Generic class references
A Class reference points to a Class object, which manufactures instances of classes and contains all the method code for those instances. It also contains the statics for that class.
public class GenericClassReferences {
public static void main(String[] args) {
Class intClass = int.class;
Class<Integer> genericIntClass = int.class;
genericIntClass = Integer.class;
intClass = double.class; // it's OK
// genericIntClass = double.class; // Illegal
}
}
By using the generic syntax, you allow the compiler to enforce extra type checking
Class<Number> genericNumberClass = int.class; // it doesn't work, because the Integer Class object is not a subclass of the Number class object
To loosen the constraints when using generic Class references, use the wildcard "?"
Class<?> intClass = int.class;
The benefit of Class<?> is that it indicates that you aren’t just using a non-specific class reference by accident, or out of ignorance. You chose the non-specific version.
Class<? extends Number> bounded = int.class // it's OK
The reason for adding the generic syntax to Class references is only to provide compile-time type checking.
Class clazz = int.class;
Class<int> intClass = int.class;
clazz.newInstance(); 返回Object
intClass.newInstance(); 返回int
public class GenericToyTest {
public static void main(String[] args) throws Exception {
Class<FancyToy> ftClass = FancyToy.class;
// Produces exact type:
FancyToy fancyToy = ftClass.newInstance();
Class<? super FancyToy> up = ftClass.getSuperclass();
// This won’t compile:
// Class<Toy> up2 = ftClass.getSuperclass();
// Only produces Object:
Object obj = up.newInstance();
}
} ///:~
If you get the superclass, the compiler will only allow you to say that the superclass reference is "some class that is a superclass of FancyToy" as seen in the expression Class <? super FancyToy >. It will not accept a declaration of Class<Toy>. This seems a bit strange because getSuperclass( ) returns the base class (not interface) and the compiler knows what that class is at compile time—in this case, Toy.class, not just "some superclass of FancyToy." In any event, because of the vagueness, the return value of up.newlnstance( ) is not a precise type, but just an Object.
New cast syntax
Class<House> houseType = House.class;
House h = houseType.cast(b);
h = (House)b; // ... or just do this.
The new casting syntax is useful for situations where you can’t just use an ordinary cast.This usually happens when you’re writing generic code, and you’ve stored a Class reference that you want to use to cast with at a later time. It turns out to be a rare thing
Class.asSubclass(): allow you to cast the class object to a more specific type.
Checking before a cast
Forms of RTTI:
1. The classic cast; e.g., "(Shape)," which uses RTTI to make sure that cast is correct. This will throw a ClassCastException if you've performed a bad cast.
2. The Class object representing the type of your object. The Class object can be queried for useful runtime information
3. instanceof. tells you if an object is an instance of a particular type.
A dynamic instanceof
i instanceof Integer // instanceof后必须写死为某个类名
Integer.class.isInstance(i) //isInstance前为引用比较灵活
Counting recursively
public class TypeCounter extends HashMap<Class<?>,Integer>{
private Class<?> baseType;
public TypeCounter(Class<?> baseType) {
this.baseType = baseType;
}
public void count(Object obj) {
Class<?> type = obj.getClass();
if(!baseType.isAssignableFrom(type))
throw new RuntimeException(obj + " incorrect type: "
+ type + ", should be type or subtype of "
+ baseType);
countClass(type);
}
private void countClass(Class<?> type) {
Integer quantity = get(type);
put(type, quantity == null ? 1 : quantity + 1);
Class<?> superClass = type.getSuperclass();
if(superClass != null &&
baseType.isAssignableFrom(superClass))
countClass(superClass);
}
public String toString() {
StringBuilder result = new StringBuilder("{");
for(Map.Entry<Class<?>,Integer> pair : entrySet()) {
result.append(pair.getKey().getSimpleName());
result.append("=");
result.append(pair.getValue());
result.append(", ");
}
result.delete(result.length()-2, result.length());
result.append("}");
return result.toString();
}
}
Registered factories
A problem with generating objects of the Pets hierarchy is the fact that every time you add a new type of Pet to the hierarchy you must remember to add it to the entries in LiteralPetCreator.java.
So the best you can probably do is to put the list in one central, obvious place. The base class for the hierarchy of interest is probably the best place.
instanceof vs. Class equivalence
instanceof says, "Are you this class, or a class derived from this class?" On the other hand, if you compare the actual Class objects using ==, there is no concern with inheritance—it’s either the exact type or it isn’t.
Reflection: runtime class information
Dynamic proxies
Proxy is an object that you insert in place of the "real" object in order to provide additional or different operations--these usually involve communication with a "real" object, so a proxy typically acts as a go-between.
class SimpleProxy implements Interface {
private Interface proxied;
public SimpleProxy(Interface proxied) {
this.proxied = proxied;
}
public void doSomething() {
print("SimpleProxy doSomething");
proxied.doSomething();
}
public void somethingElse(String arg) {
print("SimpleProxy somethingElse " + arg);
proxied.somethingElse(arg);
}
}
A proxy can be helpful anytime you’d like to separate extra operations into a different place than the "real object," and especially when you want to easily change from not using the extra operations to using them, and vice versa (the point of design patterns is to encapsulate change—so you need to be changing things in order to justify the pattern). For example, what if you wanted to track calls to the methods in the RealObject, or to measure the overhead of such calls? This is not code you want to have incorporated in your application, so a proxy allows you to add and remove it easily.
Java’s dynamic proxy takes the idea of a proxy one step further, by both creating the proxy object dynamically and handling calls to the proxied methods dynamically. All calls made on a dynamic proxy are redirected to a single invocation handler, which has the job of discovering what the call is and deciding what to do about it.
class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
public Object
invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("**** proxy: " + proxy.getClass() +
", method: " + method + ", args: " + args);
if(args != null)
for(Object arg : args)
System.out.println(" " + arg);
return method.invoke(proxied, args);
}
}
class SimpleDynamicProxy {
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("bonobo");
}
public static void main(String[] args) {
RealObject real = new RealObject();
consumer(real);
// Insert a proxy and call again:
Interface proxy = (Interface)Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[]{ Interface.class },
new DynamicProxyHandler(real));
consumer(proxy);
}
} /* Output: (95% match)
doSomething
somethingElse bonobo
**** proxy: class $Proxy0, method: public abstract void Interface.doSomething(), args: null
doSomething
**** proxy: class $Proxy0, method: public abstract void Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@42e816
bonobo
somethingElse bonobo
*///:~
You create a dynamic proxy by calling the static method Proxy.newProxyInstance( ), which requires a class loader (you can generally just hand it a class loader from an object that has already been loaded), a list of interfaces (not classes or abstract classes) that you wish the proxy to implement, and an implementation of the interface InvocationHandler. The dynamic proxy will redirect all calls to the invocation handler, so the constructor for the invocation handler is usually given the reference to the "real" object so that it can forward requests once it performs its intermediary task.
The dynamic proxy is not a tool that you’ll use every day, but it can solve certain types of problems very nicely
Null Objects(??)
Notice that you must still test for Null Objects in some places, which is not that different from checking for null, but in other places (such as toString( ) conversions, in this case), you don’t have to perform extra tests; you can just assume that all object references are valid
Mock Objects & Stubs
Logical variations of the Null Object are the MocA: Object and the Stub. Like Null Object, both of these are stand-ins for the "real" object that will be used in the finished program. However, both Mock Object and Stub pretend to be live objects that deliver real information, rather than being a more intelligent placeholder for null, as Null Object is.
The distinction between Mock Object and Stub is one of degree. Mock Objects tend to be lightweight and self-testing, and usually many of them are created to handle various testing situations. Stubs just return stubbed data, are typically heavyweight and are often reused between tests. Stubs can be configured to change depending on how they are called. So a Stub is a sophisticated object that does lots of things, whereas you usually create lots of small, simple Mock Objects if you need to do many things.
Interfaces and type information
Summary
The intent of O O programming is to use polymorphic method calls everywhere you can, and RTTI only when you must
However, using polymorphic method calls as they are intended requires that you have control of the base-class definition, because at some point in the extension of your program you might discover that the base class doesn’t include the method you need. If the base class comes from someone else’s library, one solution is RTTI: You can inherit a new type and add your extra method. Elsewhere in the code you can detect your particular type and call that special method. This doesn’t destroy the polymorphism and extensibility of the program, because adding a new type will not require you to hunt for switch statements in your program. However, when you add code that requires your new feature, you must use RTTI to detect your particular type.
Putting a feature in a base class might mean that, for the benefit of one particular class, all of the other classes derived from that base require some meaningless stub of a method. This makes the interface less clear and annoys those who must override abstract methods when they derive from that base class. For example, consider a class hierarchy representing musical instruments. Suppose you want to clear the spit valves of all the appropriate instruments in your orchestra. One option is to put a clearSpitValve( ) method in the base class Instrument, but this is confusing because it implies that Percussion, Stringed and Electronic instruments also have spit valves. RTTI provides a much more reasonable solution because you can place the method in the specific class where it’s appropriate (Wind, in this case). At the same time, you may discover that there’s a more sensible solution—here, a preparelnstrument( ) method in the base class. However, you might not see such a solution when you’re first solving the problem and could mistakenly assume that you must use RTTI.
Finally, RTTI will sometimes solve efficiency problems. Suppose your code nicely uses polymorphism, but it turns out that one of your objects reacts to this general-purpose code in a horribly inefficient way.
Instead, I think that the existence of a consistent error-reporting model empowers us to write dynamic code using reflection. Of course it’s worth trying to write code that can be statically checked ... when you can.
Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(十四)之Type Information的更多相关文章
- Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(十二)之Error Handling with Exceptions
The ideal time to catch an error is at compile time, before you even try to run the program. However ...
- Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(十)之Inner Classes
The inner class is a valuable feature because it allows you to group classes that logically belong t ...
- 《Linux命令、编辑器与shell编程》第三版 学习笔记---002
<Linux命令.编辑器与shell编程>第三版 学习笔记---001 Linux命令.编辑器与shell编程 Shell准备 1.识别Shell类型 echo $0 echo $BAS ...
- Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(十一)之Holding Your Objects
To solve the general programming problem, you need to create any number of objects, anytime, anywher ...
- Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(九)之Interfaces
Interfaces and abstract classes provide more structured way to separate interface from implementatio ...
- Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(八)之Polymorphism
Polymorphism is the third essential feature of an object-oriented programming language,after data ab ...
- Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(八)之Reusing Classes
The trick is to use the classes without soiling the existing code. 1. composition--simply create obj ...
- Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(七)之Access Control
Access control ( or implementation hiding) is about "not getting it right the first time." ...
- Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(六)之Initialization & Cleanup
Two of these safety issues are initialization and cleanup. initialization -> bug cleanup -> ru ...
随机推荐
- 3 report formats of SFDC
Choose one of the following report formats using the Format menu of the report builder. Tabular form ...
- python之面向对象的成员,方法,属性,异常处理
一.类的私有成员 1. 类中的私有成员是什么? 私有:只有满足一部分条件的才能使用 私有类的属性 私有对象的属性 私有方法 正常状态 class B: school_name = '老男孩教育' de ...
- OpenCV-Python 鼠标作画 | 八
目标 了解如何在OpenCV中处理鼠标事件 您将学习以下功能:cv.setMouseCallback() 简单演示 在这里,我们创建一个简单的应用程序,无论我们在哪里双击它,都可以在图像上绘制一个圆. ...
- 一文综述python读写csv xml json文件各种骚操作
Python优越的灵活性和易用性使其成为最受欢迎的编程语言之一,尤其是对数据科学家而言.这在很大程度上是因为使用Python处理大型数据集是很简单的一件事情. 如今,每家科技公司都在制定数据战略. ...
- 我的Keras使用总结(4)——Application中五款预训练模型学习及其应用
本节主要学习Keras的应用模块 Application提供的带有预训练权重的模型,这些模型可以用来进行预测,特征提取和 finetune,上一篇文章我们使用了VGG16进行特征提取和微调,下面尝试一 ...
- mybatis简单项目
1,mybatis MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可 ...
- iOS 缩小 ipa 大小
一.爱奇艺 爱奇艺移动应用优化之路:如何让崩溃率小于千分之二 iOS8 对于 App 的 text 段有 60MB 的限制: 超过 200MB 的 App 需要连接 WIFI 下载(之前是 150MB ...
- gold 30 mins
- G 树的难题
时间限制 : 10000 MS 空间限制 : 165536 KB 评测说明 : 1s,128m 问题描述 给出一个无根树.树有N个点,边有权值.每个点都有颜色,是黑色.白色.灰色这三种颜色之一,称 ...
- pycharm文件名颜色代表的含义
在使用pycharm过程中,文件名有不一样的颜色. 绿色:已经加入版本控制暂未提交 红色:未加入版本控制 蓝色:加入版本控制,已提交,有改动 白色:加入版本控制,已提交,无改动 灰色:版本控制已忽略文 ...