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 ...
随机推荐
- 【面试QA-基本模型】LSTM
目录 为什么传统 CNN 适用于 CV 任务,RNN 适用于 NLP 任务 RNN 原理 LSTM 原理 GRU 原理 RNN BPTT LSTM 如何解决 RNN 的梯度消失问题 怎样增加 LSTM ...
- Django之Ajax传输数据
MTV与MVC模型 MTV与MVC都是模型,只不过MTV是django自己定义的,具体看一下他们的意思 MTV模型(django) M:模型层(models.py) T:templates文件夹 V: ...
- 采用vue编写的功能强大的swagger-ui页面
think-swagger-ui-vuele swagger-ui有非常多的版本,觉得不太好用,用postman,每个接口都要自己进行录入.所以在基于think-vuele进行了swagger格式js ...
- 蓝桥杯——一步之遥,扩展gcd的应用
1. 一步之遥 [问题描述]从昏迷中醒来,小明发现自己被关在X星球的废矿车里.矿车停在平直的废弃的轨道上.他的面前是两个按钮,分别写着“F”和“B”. 小明突然记起来,这两个按钮可以控制矿车在轨道上前 ...
- Oracle数据库的创建表全
CREATE TABLE "库名"."表名" ( "FEE_ID" VARCHAR2(10 BYTE) constraint ABS_FEE ...
- maven resource filter 说明和配置方法
<maven> <dependencies> <dependency> ... </dependency> </dependencies> ...
- POJ-3134-Power Calculus(迭代加深)
题意:输入一个n,问x从1次方开始,到n次方 ,可以乘或除已经计算出来的数 ,最少需要执行多少步? 思路:迭代加深 ,深度从0开始 ,直到返回值为真. 在深搜过程中剪枝(深度的判断 ,当前最大值尽全力 ...
- RMQ(倍增法求ST)
解决什么问题:区间查询最值 倍增思想:每次得出结果的范围呈2的幂次增长,有人说相当于二分,目前我觉得相当于线段树的查找. 具体理解看代码: /*倍增法求ST*/ #include<math.h& ...
- WeChat-SmallProgram:组件 scroll-view 横向和纵向 案例
scroll-view为滚动视图,分为水平滚动和垂直滚动.注意滚动视图垂直滚动时一定要设置高度否则的话scroll-view不会生效. 滚动视图常用的地方一般都是Item项比较多的界面,比如我的模块 ...
- 1047: 【入门】正整数N转换成一个二进制数
1047: [入门]正整数N转换成一个二进制数 时间限制: 1 Sec 内存限制: 16 MB 提交: 9786 解决: 6447 [提交] [状态] [讨论版] [命题人:外部导入] 题目描述 输入 ...