运行时类型信息使得你能够在程序运行时发现和使用类型信息java
面向对象编程中基本的目的是:让代码只操做对基类的引用。
多态:程序员
import java.util.*; 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(); } } /* Output: Circle.draw() Square.draw() Triangle.draw() *///:~
Rtti基本使用形式:全部类型转换都是在运行时进行正确性检查的。在运行时识别一个对象的类型。编程
每当编写而且编译一个新类,就会产生一个Class对象。为了生成这个类的对象,运行这个程序的Java虚拟机(JVM)将使用被称为"类加载器"的子系统。
类加载器子系统实际上能够包含一条加载器链,可是只有一个原生类加载器,它是JVM实现的一部分。原生类加载器加载的所谓的可信类,它们一般是从本地盘加载的。
全部类都在第一个使用时,动态加载到JVM中。
Java程序在它开始运行以前并不是被彻底加载,其各个部分是在必须时才加载的。
类加载器首先检查这个类的class对象是否已经加载。若是没有加载,默认的类加载器就会根据类名查找.class文件。设计模式
//: typeinfo/toys/ToyTest.java // Testing class Class. package toys; 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) { System.out.println("Class name: " + cc.getName() + " is interface? [" + cc.isInterface() + "]"); System.out.println("Simple name: " + cc.getSimpleName()); System.out.println("Canonical name : " + cc.getCanonicalName()); } public static void main(String[] args) { Class c = null; try { c = Class.forName("toys.FancyToy"); } catch (ClassNotFoundException e) { System.out.println("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) { System.out.println("Cannot instantiate"); System.exit(1); } catch (IllegalAccessException e) { System.out.println("Cannot access"); System.exit(1); } printInfo(obj.getClass()); } } /* Output: Class name: typeinfo.toys.FancyToy is interface? [false] Simple name: FancyToy Canonical name : typeinfo.toys.FancyToy Class name: typeinfo.toys.HasBatteries is interface? [true] Simple name: HasBatteries Canonical name : typeinfo.toys.HasBatteries Class name: typeinfo.toys.Waterproof is interface? [true] Simple name: Waterproof Canonical name : typeinfo.toys.Waterproof Class name: typeinfo.toys.Shoots is interface? [true] Simple name: Shoots Canonical name : typeinfo.toys.Shoots Class name: typeinfo.toys.Toy is interface? [false] Simple name: Toy Canonical name : typeinfo.toys.Toy *///:~
Java还提供了一个方法来生成对Class对象的引用,即便用类字面常量。
FancyToy.class;
类字面常量不只能够应用于普通的类,也能够应用于接口、数组、基本数据类型。
对于基本数据类型的包装器类,还有一个标准字段TYPE。TYPE字段是一个引用,指向对应的基本数据类型的Class对象。
当使用.class建立对象的引用时,不会自动初始化该class对象。
为了使用类而作的准备工做实际包含三个步骤:数组
//: typeinfo/ClassInitialization.java import java.util.*; 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 *///:~
若是一个static域不是final的,那么在对它访问时,老是要求在它被读取前,先进行连接(分配存储空间)和初始化(初始化存储空间)网络
Class引用老是指向某个Class对象,它能够制造类的实例,并包含可做用于这些实例的全部方法代码。它还包含该类的静态成员,所以,Class引用表达的就是它所指向的对象的确切类型,而该对象即是Class类的一个对象。
经过容许你对Class引用所指向的Class对象的类型进行限定,将它的类型变得更具体一些。less
public class GenericClassReferences { public static void main(String[] args) { Class intClass = int.class; Class<Integer> genericIntClass = int.class;//只能指向具体的类型 genericIntClass = Integer.class; // Same thing intClass = double.class; //genericIntClass = double.class; // Illegal } } ///:~
通配符:? 表示任何事物。dom
public class WildcardClassReferences { public static void main(String[] args) { Class<?> intClass = int.class; intClass = double.class; } } ///:~
Class<?>优于平凡得class,即使他们是等价得。Class<?>的好处表达你知道你选择的是非具体的版本。
将通配符和extends关键字结合使用:ide
public class BoundedClassReferences { public static void main(String[] args) { Class<? extends Number> bounded = int.class; bounded = double.class; bounded = Number.class; // Or anything else derived from Number. } } ///:~
下面的例子,它存储了一个引用,稍后又产生了一个List,填充这个List对象使用newInstance()方法:工具
import java.util.*; class CountedInteger { private static long counter; private final long id = counter++; public String toString() { return Long.toString(id); } } public class FilledList<T> { private Class<T> type; public FilledList(Class<T> type) { this.type = type; } public List<T> create(int nElements) { List<T> result = new ArrayList<T>(); try { for(int i = 0; i < nElements; i++) result.add(type.newInstance()); } catch(Exception e) { throw new RuntimeException(e); } return result; } public static void main(String[] args) { FilledList<CountedInteger> fl = new FilledList<CountedInteger>(CountedInteger.class); System.out.println(fl.create(15)); } } /* Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] *///:~
newInstance()将返回对象的确切类型:
public class GenericToy { public static void main(String[] args) throws Exception { Class<B> b = B.class; System.out.println("a Name:" + b.getName()); B b1 = b.newInstance(); System.out.println(b); A a = (A)b.getSuperclass().newInstance(); System.out.println("getSuperclass.Name:"+a); A a1 = b.newInstance(); System.out.println(a1); Object o = b.getSuperclass(); System.out.println(o); } } class A{ static { System.out.println("AAAA"); } public String toString() { return "a"; } } class B extends A{ static { System.out.println("BBBB"); } public String toString() { return "b"; } }
Class引用的转型语法cast()方法:接受参入参数对象,转换为引用类型
class Building {} class House extends Building {} public class ClassCasts { public static void main(String[] args) { Building b = new House(); Class<House> houseType = House.class; House h = houseType.cast(b); h = (House)b; // ... or just do this. } } ///:~
Class.asSubclass():容许你将一个类对象转型为更加具体的类型。
咱们已知的RTTI形式包括:
instanceof只可将其与命名空间类型进行比较,而不能与Class对象做比较。
使用工厂方法设计模式,将对象的建立工做交给类本身去完成,工厂方法能够被多态的调用,从而为你建立恰当类型的对象。
使用工厂建立对象:
//: typeinfo/RegisteredFactories.java // Registering Class Factories in the base class. import factory.*; import java.util.*; class Part { public String toString() { return getClass().getSimpleName(); } static List<Factory<? extends Part>> partFactories = new ArrayList<Factory<? extends Part>>(); static { // Collections.addAll() gives an "unchecked generic // array creation ... for varargs parameter" warning. partFactories.add(new FuelFilter.Factory()); partFactories.add(new AirFilter.Factory()); partFactories.add(new CabinAirFilter.Factory()); partFactories.add(new OilFilter.Factory()); partFactories.add(new FanBelt.Factory()); partFactories.add(new PowerSteeringBelt.Factory()); partFactories.add(new GeneratorBelt.Factory()); } private static Random rand = new Random(47); public static Part createRandom() { int n = rand.nextInt(partFactories.size()); return partFactories.get(n).create(); } } class Filter extends Part {} class FuelFilter extends Filter { // Create a Class Factory for each specific type: public static class Factory implements factory.Factory<FuelFilter> { public FuelFilter create() { return new FuelFilter(); } } } class AirFilter extends Filter { public static class Factory implements factory.Factory<AirFilter> { public AirFilter create() { return new AirFilter(); } } } class CabinAirFilter extends Filter { public static class Factory implements factory.Factory<CabinAirFilter> { public CabinAirFilter create() { return new CabinAirFilter(); } } } class OilFilter extends Filter { public static class Factory implements factory.Factory<OilFilter> { public OilFilter create() { return new OilFilter(); } } } class Belt extends Part {} class FanBelt extends Belt { public static class Factory implements factory.Factory<FanBelt> { public FanBelt create() { return new FanBelt(); } } } class GeneratorBelt extends Belt { public static class Factory implements factory.Factory<GeneratorBelt> { public GeneratorBelt create() { return new GeneratorBelt(); } } } class PowerSteeringBelt extends Belt { public static class Factory implements factory.Factory<PowerSteeringBelt> { public PowerSteeringBelt create() { return new PowerSteeringBelt(); } } } public class RegisteredFactories { public static void main(String[] args) { for(int i = 0; i < 10; i++) System.out.println(Part.createRandom()); } } /* Output: GeneratorBelt CabinAirFilter GeneratorBelt AirFilter PowerSteeringBelt CabinAirFilter FuelFilter PowerSteeringBelt PowerSteeringBelt FuelFilter *///:~
在查询类型信息时,以instanceof的形式与直接比较Class对象有一个很重要的差异,下面看看这些差异:
//: typeinfo/FamilyVsExactType.java // The difference between instanceof and class class Base {} class Derived extends Base {} public class FamilyVsExactType { static void test(Object x) { System.out.println("Testing x of type " + x.getClass()); System.out.println("x instanceof Base " + (x instanceof Base)); System.out.println("x instanceof Derived "+ (x instanceof Derived)); System.out.println("Base.isInstance(x) "+ Base.class.isInstance(x)); System.out.println("Derived.isInstance(x) " + Derived.class.isInstance(x)); System.out.println("x.getClass() == Base.class " + (x.getClass() == Base.class)); System.out.println("x.getClass() == Derived.class " + (x.getClass() == Derived.class)); System.out.println("x.getClass().equals(Base.class)) "+ (x.getClass().equals(Base.class))); System.out.println("x.getClass().equals(Derived.class)) " + (x.getClass().equals(Derived.class))); } public static void main(String[] args) { test(new Base()); test(new Derived()); } } /* Output: Testing x of type class typeinfo.Base x instanceof Base true x instanceof Derived false Base.isInstance(x) true Derived.isInstance(x) false x.getClass() == Base.class true x.getClass() == Derived.class false x.getClass().equals(Base.class)) true x.getClass().equals(Derived.class)) false Testing x of type class typeinfo.Derived x instanceof Base true x instanceof Derived true Base.isInstance(x) true Derived.isInstance(x) true x.getClass() == Base.class false x.getClass() == Derived.class true x.getClass().equals(Base.class)) false x.getClass().equals(Derived.class)) true *///:~
在编译时,编译器必须知道所要经过RTTI来处理的类。
反射提供了一种机制——用来检查可用的方法,并返回方法名。
人们想要在运行时获取类的信息的另外一个动机,但愿在跨网络的平台上建立和运行对象的能力。这被称为远程方法调用(RMI),它容许将一个Java程序分布到多台机器上。
Class类与java.lang.reflect类库一块儿对反射概念进行了支持。这些类型的对象都是又JVM在运行时建立的,用以表示未知类里对应的成员。
当经过反射与一个未知类型的对象打交道时,JVM只是简单的检查这个对象,看它属于哪一个特定的类。在用它作其余事情以前必须先加载那个类的Class对象。所以,那个类的.Class文件对于JVM来讲必须是可获取的:要么在本地机器上,要么能够经过网络获取。因此RTTI和反射之间真正的区别只在于du:对RTTI来讲,编译器在编译时打开和检查.class文件,对于反射来讲:.class在编译时是不可获取的,全部在运行时打开和检查.class文件。
反射机制提供了一种方法,使咱们可以编写能够自动展现完整接口的简单工具:
代理是基本的设计模式之一,它是你为了提供额外的或不一样的操做,而插入的用来代替实际对象的对象。
interface Interface { void doSomething(); void somethingElse(String arg); } class RealObject implements Interface { public void doSomething() { System.out.println("doSomething"); } public void somethingElse(String arg) { System.out.println("somethingElse " + arg); } } class SimpleProxy implements Interface { private Interface proxied; public SimpleProxy(Interface proxied) { this.proxied = proxied; } public void doSomething() { System.out.println("SimpleProxy doSomething"); proxied.doSomething(); } public void somethingElse(String arg) { System.out.println("SimpleProxy somethingElse " + arg); proxied.somethingElse(arg); } } class SimpleProxyDemo { public static void consumer(Interface iface) { iface.doSomething(); iface.somethingElse("bonobo"); } public static void main(String[] args) { consumer(new RealObject()); consumer(new SimpleProxy(new RealObject())); } }
Java的动态代理能够动态建立并动态地处理对所代理方法的调用。在动态代理上所作的全部调用都会被重定向到单一的调用处理器上,它的工做是揭示调用的类型并肯定相应的对策。
import java.lang.reflect.*; 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); } }
动态代理能够将全部调用重定向到调用处理器,所以一般会向调用处理器的构造器传递一个实际对象的引用,从而使得调用处理器在执行其中介任务时,能够将请求转发。
引入空对象的思想是颇有用的,它能够接收传递给它的所表明的对象的消息,可是将返回表示为实际上并不存在的任何真实对象的值。经过这种方式,你能够假设全部的对象都是有效的,而没必要浪费时间去检查null。
即便空对象能够响应实际对象能够响应的全部消息,任须要测试是否为空:
public interface Operation { String description(); void command(); }
class Person { public final String first; public final String last; public final String address; // etc. public Person(String first, String last, String address){ this.first = first; this.last = last; this.address = address; } public String toString() { return "Person: " + first + " " + last + " " + address; } public static class NullPerson extends Person implements Null { private NullPerson() { super("None", "None", "None"); } public String toString() { return "NullPerson"; } } public static final Person NULL = new NullPerson(); }
class Position { private String title; private Person person; public Position(String jobTitle, Person employee) { title = jobTitle; person = employee; if(person == null) person = Person.NULL; } public Position(String jobTitle) { title = jobTitle; person = Person.NULL; } public String getTitle() { return title; } public void setTitle(String newTitle) { title = newTitle; } public Person getPerson() { return person; } public void setPerson(Person newPerson) { person = newPerson; if(person == null) person = Person.NULL; } public String toString() { return "Position: " + title + " " + person; } }
//: typeinfo/Staff.java import java.util.*; public class Staff extends ArrayList<Position> { public void add(String title, Person person) { add(new Position(title, person)); } public void add(String... titles) { for(String title : titles) add(new Position(title)); } public Staff(String... titles) { add(titles); } public boolean positionAvailable(String title) { for(Position position : this) if(position.getTitle().equals(title) && position.getPerson() == Person.NULL) return true; return false; } public void fillPosition(String title, Person hire) { for(Position position : this) if(position.getTitle().equals(title) && position.getPerson() == Person.NULL) { position.setPerson(hire); return; } throw new RuntimeException( "Position " + title + " not available"); } public static void main(String[] args) { Staff staff = new Staff("President", "CTO", "Marketing Manager", "Product Manager", "Project Lead", "Software Engineer", "Software Engineer", "Software Engineer", "Software Engineer", "Test Engineer", "Technical Writer"); staff.fillPosition("President", new Person("Me", "Last", "The Top, Lonely At")); staff.fillPosition("Project Lead", new Person("Janet", "Planner", "The Burbs")); if(staff.positionAvailable("Software Engineer")) staff.fillPosition("Software Engineer", new Person("Bob", "Coder", "Bright Light City")); System.out.println(staff); } } /* Output: [Position: President Person: Me Last The Top, Lonely At, Position: CTO NullPerson, Position: Marketing Manager NullPerson, Position: Product Manager NullPerson, Position: Project Lead Person: Janet Planner The Burbs, Position: Software Engineer Person: Bob Coder Bright Light City, Position: Software Engineer NullPerson, Position: Software Engineer NullPerson, Position: Software Engineer NullPerson, Position: Test Engineer NullPerson, Position: Technical Writer NullPerson] *///:~
使用命令模式:
//: typeinfo/Robot.java import java.util.*; public interface Robot { String name(); String model(); List<Operation> operations(); class Test { public static void test(Robot r) { if(r instanceof Null) System.out.println("[Null Robot]"); System.out.println("Robot name: " + r.name()); System.out.println("Robot model: " + r.model()); for(Operation operation : r.operations()) { System.out.println(operation.description()); operation.command(); } } } } ///:~
//: typeinfo/SnowRemovalRobot.java import java.util.*; public class SnowRemovalRobot implements Robot { private String name; public SnowRemovalRobot(String name) {this.name = name;} public String name() { return name; } public String model() { return "SnowBot Series 11"; } public List<Operation> operations() { return Arrays.asList( new Operation() { public String description() { return name + " can shovel snow"; } public void command() { System.out.println(name + " shoveling snow"); } }, new Operation() { public String description() { return name + " can chip ice"; } public void command() { System.out.println(name + " chipping ice"); } }, new Operation() { public String description() { return name + " can clear the roof"; } public void command() { System.out.println(name + " clearing roof"); } } ); } public static void main(String[] args) { Robot.Test.test(new SnowRemovalRobot("Slusher")); } } /* Output: Robot name: Slusher Robot model: SnowBot Series 11 Slusher can shovel snow Slusher shoveling snow Slusher can chip ice Slusher chipping ice Slusher can clear the roof Slusher clearing roof *///:~
//: typeinfo/NullRobot.java // Using a dynamic proxy to create a Null Object. import java.lang.reflect.*; import java.util.*; class NullRobotProxyHandler implements InvocationHandler { private String nullName; private Robot proxied = new NRobot(); NullRobotProxyHandler(Class<? extends Robot> type) { nullName = type.getSimpleName() + " NullRobot"; } private class NRobot implements Null, Robot { public String name() { return nullName; } public String model() { return nullName; } public List<Operation> operations() { return Collections.emptyList(); } } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(proxied, args); } } public class NullRobot { public static Robot newNullRobot(Class<? extends Robot> type) { return (Robot)Proxy.newProxyInstance( NullRobot.class.getClassLoader(), new Class[]{ Null.class, Robot.class }, new NullRobotProxyHandler(type)); } public static void main(String[] args) { Robot[] bots = { new SnowRemovalRobot("SnowBee"), newNullRobot(SnowRemovalRobot.class) }; for(Robot bot : bots) Robot.Test.test(bot); } } /* Output: Robot name: SnowBee Robot model: SnowBot Series 11 SnowBee can shovel snow SnowBee shoveling snow SnowBee can chip ice SnowBee chipping ice SnowBee can clear the roof SnowBee clearing roof [Null Robot] Robot name: SnowRemovalRobot NullRobot Robot model: SnowRemovalRobot NullRobot *///:~
空对象的逻辑变体是模拟对象和桩。模拟对象和桩都只是假扮能够传递实际信息的存活对象,而不是像空对象那样能够称为null的一种更加智能化的替代品。
interface关键字的一种重要目标就是容许程序员隔离构建,进而下降耦合性。经过类型信息,这种耦合性仍是会传播出去——接口并不是是对解耦的一种无懈可击的保障。
package interfacea; public interface A { void f(); }
import interfacea.*; class B implements A { public void f() {} public void g() {} } public class InterfaceViolation { public static void main(String[] args) { A a = new B(); a.f(); // a.g(); // Compile error System.out.println(a.getClass().getName()); if(a instanceof B) { B b = (B)a; b.g(); } } }
最简单的方式是对实现使用包访问权限,这样在包外部的客户端就不能看见它了。
package packageaccess; import interfacea.*; class C implements A { public void f() { System.out.println("public C.f()"); } public void g() { System.out.println("public C.g()"); } void u() { System.out.println("package C.u()"); } protected void v() { System.out.println("protected C.v()"); } private void w() { System.out.println("private C.w()"); } } public class HiddenC { public static A makeA() { return new C(); } }
这里makeA返回C类型,但在包在并不能使用到C。可是反射却仍旧能够调用:
import interfacea.*; import packageaccess.*; import java.lang.reflect.*; public class HiddenImplementation { public static void main(String[] args) throws Exception { A a = HiddenC.makeA(); a.f(); System.out.println(a.getClass().getName()); // Compile error: cannot find symbol 'C': /* if(a instanceof C) { C c = (C)a; c.g(); } */ // Oops! Reflection still allows us to call g(): callHiddenMethod(a, "g"); // And even methods that are less accessible! callHiddenMethod(a, "u"); callHiddenMethod(a, "v"); callHiddenMethod(a, "w"); } static void callHiddenMethod(Object a, String methodName) throws Exception { Method g = a.getClass().getDeclaredMethod(methodName); g.setAccessible(true); g.invoke(a); } } /* Output: public C.f() typeinfo.packageaccess.C public C.g() package C.u() protected C.v() private C.w() *///:~
即便是内部类,反思仍旧能够调用到:
import interfacea.*; class InnerA { private static class C implements A { public void f() { System.out.println("public C.f()"); } public void g() { System.out.println("public C.g()"); } void u() { System.out.println("package C.u()"); } protected void v() { System.out.println("protected C.v()"); } private void w() { System.out.println("private C.w()"); } } public static A makeA() { return new C(); } } public class InnerImplementation { public static void main(String[] args) throws Exception { A a = InnerA.makeA(); a.f(); System.out.println(a.getClass().getName()); // Reflection still gets into the private class: HiddenImplementation.callHiddenMethod(a, "g"); HiddenImplementation.callHiddenMethod(a, "u"); HiddenImplementation.callHiddenMethod(a, "v"); HiddenImplementation.callHiddenMethod(a, "w"); } }
匿名类也同样:
import interfacea.*; class AnonymousA { public static A makeA() { return new A() { public void f() { System.out.println("public C.f()"); } public void g() { System.out.println("public C.g()"); } void u() { System.out.println("package C.u()"); } protected void v() { System.out.println("protected C.v()"); } private void w() { System.out.println("private C.w()"); } }; } } public class AnonymousImplementation { public static void main(String[] args) throws Exception { A a = AnonymousA.makeA(); a.f(); System.out.println(a.getClass().getName()); // Reflection still gets into the anonymous class: HiddenImplementation.callHiddenMethod(a, "g"); HiddenImplementation.callHiddenMethod(a, "u"); HiddenImplementation.callHiddenMethod(a, "v"); HiddenImplementation.callHiddenMethod(a, "w"); } } /* Output: public C.f() AnonymousA$1 public C.g() package C.u() protected C.v() private C.w() *///:~