上一章,Java编程思想(四) —— 复用类里面讲到了向上转型,感觉和多态放在一起写更好。
多态,polymorphism。一个重要的特性,篇幅太长了,分上下两篇写。
(1)向上转型
class TV{ public static void show(TV tv){ System.out.println("TV"); } } public class LeTV extends TV{ public static void main(String[] args) { LeTV letv = new LeTV(); TV.show(letv); } }
看到没有,程序正常运行。本来参数类型是TV类型,但是子类传进去也可以。这种将LeTV转为父类TV的行为,就是向上转型。
向上转型是安全的,子类必须继承父类的方法(这是继承的定义),自己可以拥有自己的属性和方法,从一个特定的类向一个通用的类转换,是安全的。反过来有普通到特殊却有不对应的风险。
(2)多态
先看例子:
class TV{ public void show(){ System.out.println("TV"); } } class LeTV extends TV{ public void show(){ System.out.println("LeTV"); } } class MiTV extends TV{ public void show(){ System.out.println("MiTV"); } } class SanTV extends TV{ public void show(){ System.out.println("SanTV"); } } public class EveryTV { public static void tvshow(LeTV tv){ tv.show(); } public static void tvshow(MiTV tv){ tv.show(); } public static void tvshow(SanTV tv){ tv.show(); } public static void main(String[] args) { tvshow(new LeTV()); tvshow(new MiTV()); tvshow(new SanTV()); } }
程序没什么问题,每个方法都有对应的类型。但是除了这三个子类,还有几十,几百个TV子类,那么tvshow方法岂不是也要写上对应的几十甚至几百个?
然后,多态出现了。
public class EveryTV { public static void tvshow(TV tv){ tv.show(); } public static void main(String[] args) { tvshow(new LeTV()); tvshow(new MiTV()); tvshow(new SanTV()); } }
程序简洁了很多,并且运行结果和上面的一样。美好了许多。再看一看,这和向上转型很像。
多态,可以理解成多种形式,多种状态。多态也叫动态绑定、后期绑定、运行时绑定。
谈到绑定就要联系前期绑定(前期绑定国内的搜索真的是惨不忍睹,把编程思想的话全部抄进去,不会去真正理解前期绑定是什么!)。
what is Early and Late Binding?(来自stackoverflow)。
The short answer is that early (or static) binding refers to compile time binding and late (or dynamic) binding refers to runtime binding (for example when you use reflection)——byChristian
Hagelid.
简单说前期绑定是编译期的绑定,后期绑定是运行时的绑定。
书上指出C的方法全部都是前期绑定,如果上面多态的例子是前期绑定的话,编译时就要绑定,但是tv这个引用只有一个,即使你传进去的是LeTV,它也不知道是要执行TV的show方法还是其他类的show方法。
正因为有了多态,后期绑定的存在,会根据对象来执行对应的方法。
因为多态的存在,我们可以添加多个新的子类而不用去担心要写多少tvshow方法。
向上转型的写法:
public static void main(String[] args) { TV letv = new LeTV(); TV mitv = new MiTV(); TV santv = new SanTV(); tvshow(letv); tvshow(mitv); tvshow(santv); }
各种各样的TV还是TV,可以向上转型,因为多态,又会调用各自的对象的show方法。
书上的原话讲得很好,我照抄一下,多态是一种让程序员“将改变的事物和未变的事物分离开来”的重要技术。
就上述而言,各自的TV的show方法有自己的重写,但是EveryTV这个类里面的tvshow方法却不用变动。就是多态的用处所在。
是不是觉得没什么用?
List al = new ArrayList(); List ll = new LinkedList();
用处就在这里,我先用al也行,ll也行,以后要改可以改,不然很多东西都要改过。
(3)缺陷
1)“覆盖”私有方法
书上举的例子有点牵强,方法不算重写。
public class TV{ private void show(){ System.out.println("TV"); } public static void main(String[] args) { TV tv = new LeTV(); tv.show(); } } class LeTV extends TV{ public void show(){ System.out.println("LeTV"); } }
学了多态之后,一看结果是LeTV吧。错了,LeTV根本没有重写show方法,父类的show方法是private的,不可见,不可继承。假的“覆写”。
2)域与静态方法
对域,field概念模糊的,可以看看:java中的域是什么?
public class TV{ public int price = 10; public int getprice(){ return price; } public static String getString(){ return "tv"; } } class LeTV extends TV{ public int price = 20; public int getprice(){ return price; } public int getsuperprice(){ return super.price; } public static String getString(){ return "letv"; } public static void main(String[] args) { TV tv = new LeTV(); System.out.println(tv.price+" getprice:"+tv.getprice()+tv.getString()); LeTV letv = new LeTV(); System.out.println(letv.price+" getprice:"+letv.getprice()+" getsuperprice:"+letv.getsuperprice() +letv.getString()); } }
按照多态,tv.price的结果应该为20,但是结果却是10,因为域访问操作是由编译器解析,不是多态,多态是后期绑定,也就是运行期间。
而tv.getString方法也一样,并没有打印出letv,而是tv,因为方法是静态方法,只和类有关,与具体的对象不关联。
这是运用多态时要注意的两个地方。
刚开始看这个多态特性的时候难看懂,也不知道这东西有什么用,其实程序敲多了,其义自现。你会发现你写的东西导出都在用它。