java 关于在java多态性里用循环判断调用枚举类型,在枚举类型数量多的情况下我想寻求更简便的处理方式

JAVA中,为什么要用多态,,怎么用请给个通俗易懂的例子解释下……_百度知道
JAVA中,为什么要用多态,,怎么用请给个通俗易懂的例子解释下……
多态在两方面有体现:继承多态同一种抽象的东西在具体的类型中有不同的表现。比如大多数动物(抽象类)会叫,但是狗(实现类)就是汪汪汪,猫(实现类)就是喵喵喵或者是具有包含关系需要修改表现,比如物品(父类)具有现实详细信息的功能,但是书籍(子类)显示详细信息中可能还要包含书籍本身特有信珐阀粹合诔骨达摊惮揩息比如页数、作者等等,这也是多态。重载多态:同一个方法名在不同类型不同数量的参数传入需要有不同的表现。这个在构造方法上用的挺多,一般来说类都会有一个无参构造方法,还有不定的几个有参构造方法,这是因为在编程期间需要调用不同的构造方法来产生不同的类。比如无参的就吧所有属性置为默认值,有参的就用参数去初始化属性等
其他类似问题
为您推荐:
其他5条回答
举个例子,显示鸟的飞行动作
对于八哥鸟,你可以这样new Myna().fly();
对于鹦鹉,你可以这样new Parrot().fly();
对于孔雀,你可以这样 new Peacock().fly();
对于每一个具体的鸟,你都必须调用具体的鸟类对象。
现在使用多态再来看看。
我们先写一个Bird类,用来抽取出鸟类的飞行动作。
public abstract class Bird {
public void fly() {
System.out.println(&default bird fly!&);
再写具体的鸟类,继承Bird类
class Parrot extends Bird {
public void fly() {
System.out.println(&Parrot fly&);
class Myna extends Bird {
public void fly() {
System.out.println(&Myna fly&);
现在再来看一下八哥和...
多态就是继承的表现形式。爸爸的3个儿子 各自长的不一样,这就是多态
多态--同一类多种形态比如,Animal bord = new Bord();就是啊
java语言本身是不支持多态的,但是可以使用interface实现类似多态。
使用多态就是用父类类型屏蔽子类差异,使程序更加简单通用,例如:我定义一个Animal的父类,然后再定义一个Dog和Cat的子类,当我想应用Dog的对象时,可以用父类的Animal指向Dog子类,这样就屏蔽掉了子类中的差异
多态的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁java简答题_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
软件设计师
评价文档:
java简答题
阅读已结束,如果下载本文需要使用
想免费下载本文?
文档试读已结束,请登录后查看剩余内容!
把文档贴到Blog、BBS或个人站等:
普通尺寸(450*500pix)
较大尺寸(630*500pix)
你可能喜欢Posts - 610, Comments - 29668
所谓Go语言式的接口,就是不用显示声明类型T实现了接口I,只要类型T的公开方法完全满足接口I的要求,就可以把类型T的对象用在需要接口I的地方。这种做法的学名叫做,有人也把它看作是一种静态的Duck Typing。除了Go的接口以外,类似的东西也有比如Scala里的Traits等等。有人觉得这个特性很好,但我个人并不喜欢这种做法,所以在这里谈谈它的缺点。当然这跟动态语言静态语言的讨论类似,不能简单粗暴的下一个“好”或“不好”的结论。
那么就从头谈起:什么是接口。其实通俗地讲,接口就是一个协议,规定了一组成员,例如.NET里的ICollection接口:
interface ICollection {
int Count { get; }
object SyncRoot { get; }
bool IsSynchronized { get; }
void CopyTo(Array array, int index);
这就是一个协议的全部了吗?事实并非如此,其实接口还规定了每个行为的“特征”。打个比方,这个接口的Count除了需要返回集合内元素的数目以外,还隐含了它需要在O(1)时间内返回这个要求。这样一个使用了ICollection接口的方法才能放心地使用Count属性来获取集合大小,才能在知道这些特征的情况下选用正确的算法来编写程序,而不用担心带来性能问题,这才能实现所谓的“面向接口编程”。当然这种“特征”并不单指“性能”上的,例如Count还包含了“不修改集合内容”这种看似十分自然的隐藏要求,这都是ICollection协议的一部分。
由此我们还可以解释另外一些问题,例如为什么.NET里的List&T&不叫做ArrayList&T&(当然这些都只是我的推测)。我的想法是,由于List&T&与IList&T&接口是配套出现的,而像IList&T&的某些方法,例如索引器要求能够快速获取元素,这样使用IList&T&接口的方法才能放心地使用下标进行访问,而满足这种特征的数据结构就基本与数组难以割舍了,于是名字里的Array就显得有些多余。
假如List&T&改名为ArrayList&T&,那么似乎就暗示着IList&T&可以有其他实现,难道是LinkedList&T&吗?事实上,LinkedList&T&根本与IList&T&没有任何关系,因为它的特征和List&T&相差太多,它有的尽是些AddFirst、InsertBefore方法等等。当然,LinkedList&T&与List&T&都是ICollection&T&,所以我们可以放心地使用其中一小部分成员,它们的行为特征是明确的。
这方面的反面案例之一便是Java了。在Java类库中,ArrayList和LinkedList都实现了List接口,它们都有get方法,传入一个下标,返回那个位置的元素,但是这两种实现中前者耗时O(1)后者耗时O(N),两者大相近庭。那么好,我现在要实现一个方法,它要求从第一个元素开始,返回每隔P个位置的元素,我们还能面向List接口编程么?假如我们依赖下标访问,则外部一不小心传入LinkedList的时候,算法的时间复杂度就从期望的O(N/P)变成了O(N2/P)。假如我们选择遍历整个列表,则即便是ArrayList我们也只能得到O(N)的效率。话说回来,Java类库的List接口就是个笑话,连Stack类都实现了List,真不知道当年的设计者是怎么想的。
简单地说,假如接口不能保证行为特征,则“面向接口编程”没有意义。
而Go语言式的接口也有类似的问题,因为Structural Typing都只是从表面(成员名,参数数量和类型等等)去理解一个接口,并不关注接口的规则和含义,也没法检查。忘了是Coursera里哪个课程中提到这么一个例子:
interface IPainter {
void Draw();
interface ICowBoy {
void Draw();
在英语中Draw同时具有“画画”和“拔枪”的含义,因此对于画家(Painter)和牛仔(Cow Boy)都可以有Draw这个行为,但是两者的含义截然不同。假如我们实现了一个“小明”类型,他明明只是一个画家,但是我们却让他去跟其他牛仔决斗,这样就等于让他去送死嘛。另一方面,“小王”也可以既是一个“画家”也是个“牛仔”,他两种Draw都会,在C#里面我们就可以把他实现为:
class XiaoWang : IPainter, ICowBoy {
void IPainter.Draw() {
void ICowBoy.Draw() {
因此我也一直不理解Java的取舍标准。你说这样一门强调面向对象强调接口强调设计的语言,还要求强制异常,怎么就不支持接口的显示实现呢?
这就是我更倾向于Java和C#中显式标注异常的原因。因为程序是人写的,完全不会因为一个类只是因为存在某些成员,就会被当做某些接口去使用,一切都是经过“设计”而不是自然发生的。就好像我们在泰国不会因为一个人看上去是美女就把它当做女人,这年头的化妆和PS技术太可怕了。
我这里再小人之心一把:我估计有人看到这里会说我只是酸葡萄心理,因为C#中没有这特性所以说它不好。还真不是这样,早在当年我还没听说Structural Typing这学名的时候就考虑过这个问题。我写了一个辅助方法,它可以将任意类型转化为某种接口,例如:
XiaoMing xm = new XiaoMing();
ICowBoy cb = StructuralTyping.From(xm).To&ICowBoy&();
于是,我们就很快乐地将只懂画画的小明送去决斗了。其内部实现原理很简单,只是使用Emit在运行时动态生成一个封装类而已。此外,我还在编译后使用分析程序集,检查From与To的泛型参数是否匹配,这样也等于提供了编译期的静态检查。此外,我还支持了协变逆变,还可以让不需要返回值的接口方法兼容带有返回值的方法(现在甚至还可以为其查找扩展方法),这可比简单通过名称和参数类型判断要强大多了。
有了多种选择,我才放心地说我喜欢哪个。JavaScript中只能用回调编写代码,于是很多人说它是JavaScript的优点,说回调多么多么美妙我会深不以为然——只是没法反抗开始享受罢了嘛……
这篇文章好像吐槽有点多?不过这小文章还挺爽的。
Categories:
,登陆后便可删除或修改已发表的评论
(请注意保留评论内容)Icansoft 的BLOG
用户名:Icansoft
文章数:127
评论数:232
访问量:2893722
注册日期:
阅读量:3766
阅读量:1339
阅读量:9977
阅读量:574
51CTO推荐博文
很多时候我们定义了一组值来表示用于特定的数值,往往都是习惯性地使用常量:
private static final int COLOR_WHITE = Color.WHITE;private static final int COLOR_BLACK = Color.BLACK;
后来才知道原来这样会使得类型不安全,你必须确保是int,而且还要确保它的范围必须正确。
private void updateBackgourndColor(int color){&&&&&&&&if (color == COLOR_WHITE || color == COLOR_BLACK)&&&&&&&&&&&&&&&&frameView.setBackgroundColor(color);&&&&&&&&}}
像以上的代码,一旦color定义的常量以后增加了,这里就需要相应地修改。而enum可以很方便地解决。
首先看看最简单的enum用法:
public class EnumMonth{&&&&&&&&public static void main(String[] args) {&&&&&&&&&&&&&&&&for (Month month : Month.values()) {&&&&&&&&&&&&&&&&&&&&&&&&System.out.println(month);&&&&&&&&&&&&&&&&}&&&&&&&&}&&&&&&&&private enum Month {&&&&&&&&&&&&&&&&JAN, SEP, MAR, APR, MAY;&&&&&&&&}}运行结果:
使用javap EnumMonth$Month看到反编译的结果,Month会被编译成一个Java类:
Compiled from "EnumMonth.java"final class EnumMonth$Month extends java.lang.Enum{&&&&&&&&public static final EnumMonth$Month JAN;&&&&&&&&public static final EnumMonth$Month SEP;&&&&&&&&public static final EnumMonth$Month MAR;&&&&&&&&public static final EnumMonth$Month APR;&&&&&&&&public static final EnumMonth$Month MAY;&&&&&&&&public static EnumMonth$Month[] values();&&&&&&&&public static EnumMonth$Month valueOf(java.lang.String);&&&&&&&&static {};}
1. 这个Month枚举类是final class,即不能被继承,而它本身是继承于Enum;
2. 这些枚举值是Month对象,而且是static final修饰的;
3. 注意values()方法,它能得到所有的枚举值,但这方法在Enum里面没有找到,我是在看到反编译结果后才确认的。
而valueOf(String)方法,返回带指定名称的指定枚举类型的枚举常量。在使用javap -c EnumMonth$Month进一步查看汇编代码后,知道原来是调用Enum.valueOf(Class&T& enumType, String name):
public static EnumMonth$Month valueOf(java.lang.String);&&&&Code:&&&& 0:&&ldc_w& //class EnumMonth$Month&&&& 3:&&aload_0&&&& 4:&&invokestatic& //Method java/lang/Enum.valueOf:(Ljava/lang/CLjava/lang/S)Ljava/lang/E&&&& 7:&&checkcast& //class EnumMonth$Month&&&& 10:&&areturn
static{}里面主要是new这些枚举值对象。
0:&&new& //class EnumMonth$Month3:&&dup4:&&ldc& //String JAN6:&&iconst_07:&&invokespecial& //Method "&init&":(Ljava/lang/SI)V10:&&putstatic&	 //Field JAN:LEnumMonth$M
这里要说说Enum的构造函数Enum(String name, int ordinal),形参name就是枚举值的string形式("JAN"),ordinal值则是编译器按照枚举值的顺序从0排着赋值的。要注意,这个ordinal值原来是不能改变的,即编译器是从0升序赋值的,在字节码里面固定写着iconst_0(n=0,1,2...)的。
static{&&&&&&&&JAN = new EnumMonth$Month("JAN", 0);&&&&&&&&SEP = new EnumMonth$Month("SEP", 1);&&&&&&&&...}于是Enum里面的String name()和int ordinal()方法就是返回这两个值的。Enum.toString()也是返回name值,这就是为什么System.out.println(month);会输出那些值。
看看下面的例子:
public class EnumMonth{&&&&&&&&public static void main(String[] args) {&&&&&&&&&&&&&&&&for (Month month : Month.values()) {&&&&&&&&&&&&&&&&&&&&&&&&System.out.println(month.ordinal());&&&&&&&&&&&&&&&&}&&&&&&&&}&&&&&&&&private enum Month {&&&&&&&&&&&&&&&&JAN(1), SEP(9), MAR(3), APR(4), MAY(5);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&final int&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&Month(int id){&&&&&&&&&&&&&&&&&&&&&&&&this.id =&&&&&&&&&&&&&&&&}&&&&&&&&}}一开始没有加上id成员变量和构造函数的,编译时提示找不到构造函数 Month(int),我当初还以为JAN(1)的1会赋值给形参ordinal呢!修正后,运行结果是
说明ordinal值是固定不变的。
查看汇编代码:
0:&&&& new&&&&&&&& #4; //class EnumMonth$Month3:&&&& dup4:&&&& ldc&&&&&&&& #8; //String JAN6:&&&& iconst_07:&&&& iconst_18:&&&& invokespecial&&&& #9; //Method "&init&":(Ljava/lang/SII)V11:&&&&putstatic&&&&&&&&&&&& #10; //Field JAN:LEnumMonth$M这时发现构造函数似乎增加一个形参int类型id。
我再尝试把int id改为char,运行结果是一样的:
private enum Month { &&&&&&&&JAN('1'), SEP('9'), MAR('3'), APR('4'), MAY('5'); &&&&&&&&final char &&&&&&&&Month(char id){ &&&&&&&&&&&&&&&&this.id = &&&&&&&&} }
但编译代码有所不同:
0:&&&& new&&&&&&&& #4; //class EnumMonth$Month3:&&&& dup4:&&&& ldc&&&&&&&& #8; //String JAN6:&&&& iconst_07:&&&& bipush&&&&499:&&&& invokespecial&&&& #9; //Method "&init&":(Ljava/lang/SIC)V12:&&&&putstatic&&&&&&&&&&&& #10; //Field JAN:LEnumMonth$M所以个人认为,如果我们添加了自定义的构造函数,编译器会自动构建新的构造函数:
Enum(String name, int ordinal, char id) {&&&&&&&&super(name, ordinal);&&&&&&&&&&&&&&&&//copy 自定义构造函数的内容&&&&&&&&this.id =}650) this.width=650;" src="/editor/icons/etc_12.gif" border="0" alt="" />注意:构造器只能私有private,绝对不允许有public构造器。否则提示modifier public not allowed here。
现在了解了枚举值就是该枚举类的对象,可以传常量数值作为枚举对象的属性以及自定义构造函数,接下来看看我们平时常用的switch例子:
switch(color) { case RED: &&&&&&&&System.out.println("红色"); &&&&&&&&break; case GREEN: &&&&&&&&System.out.println("绿色"); &&&&&&&&break; case BLACK: &&&&&&&&System.out.println("黑色"); &&&&&&&&break; ... default: &&&&&&&&break; }
实际上用以下代码可以解决,每个枚举对象都包含desc属性值和getDesc()方法:
public class EnumColor { &&&&&&&&public static void main(String[] args) { &&&&&&&&&&&&&&&&for (Color color : Color.values()) &&&&&&&&&&&&&&&&&&&&&&&&System.out.println(color + " is : " + color.getDesc()); &&&&&&&&} &&&&&&&&private enum Color { &&&&&&&&&&&&&&&&RED("红色"), GREEN("绿色"), BLUE("蓝色"), YELLOW("黄色"), BLACK("黑色"), WHITE("白色"); &&&&&&&&&&&&&&&&private final S &&&&&&&&&&&&&&&&private Color(String desc) { &&&&&&&&&&&&&&&&&&&&&&&&this.desc = &&&&&&&&&&&&&&&&} &&&&&&&&&&&&&&&&public String getDesc() { &&&&&&&&&&&&&&&&&&&&&&&&return &&&&&&&&&&&&&&&&} &&&&&&&&} }
运行结果:
RED is : 红色GREEN is : 绿色BLUE is : 蓝色YELLOW is : 黄色BLACK is : 黑色WHITE is : 白色
以上的例子提醒我们,不需要在外面的代码添加switch逻辑来判断以赋予不同的值,直接在enum里面处理就完成了。650) this.width=650;" height="19" alt="" src="/editor/icons/etc_12.gif" width="19" border="0" />注意:在case标签中,枚举前缀不能出现,即case Color.RED是不合法的,只能直接用枚举值RED。而在其他地方出现时则必须用Color.RED。650) this.width=650;" src="/editor/icons/etc_12.gif" border="0" alt="" />为什么switch可以支持enum呢?switch其实是支持int基本类型,而因为byte,short,char可以向上转换为int,所以switch也支持它们,但long因为转换int会截断便不能支持。&& &而enum在switch中也是int类型,看了《》,应该这样解释:&& &我使用jd反编译上面那段switch代码:??? = EnumColor.Color.RED;switch (EnumColor.1.$SwitchMap$EnumColor$Color[???.ordinal()]) {case 1:&&&&System.out.println("红色");&&&&break;case 2:&&&&System.out.println("绿色");&&&&break;case 3:&&&&System.out.println("黑色");&&&&break;}可以看到case后面的值变成int值,但这个数值并不是枚举值的ordinal。jd没有把EnumColor$1里面的代码反编译出来,但看了文章里面那段static int[] $SWITCH_TABLE$meiju$EnumTest(),原来这里面有一个int数组,按照枚举值的ordinal值作为索引值,依次给数组元素赋值从1开始: ai[Color.RED.ordinal()] = 1; ai[Color.GREEN.ordinal()] = 2; ...再看看switch括号里面的就是数组[???.ordinal()],明了!
下面说一下方法枚举(多态),我一开始写的是以下代码:public class EnumGrade {&&&&&&&&public static void main(String[] args) {&&&&&&&&&&&&&&&&for (Grade grade : Grade.values())&&&&&&&&&&&&&&&&&&&&&&&&System.out.println(grade + "'s result is : " + grade.getResult());&&&&&&&&}&&&&&&&&private enum Grade {&&&&&&&&&&&&&&&&A(1) {&&&&&&&&&&&&&&&&&&&&&&&&public int getResult() {&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&return&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&},&&&&&&&&&&&&&&&&B(3) {&&&&&&&&&&&&&&&&&&&&&&&&public int getResult() {&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&return base*2;&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&},&&&&&&&&&&&&&&&&C(5) {&&&&&&&&&&&&&&&&&&&&&&&&public int getResult() {&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&return base*3;&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&};&&&&&&&&&&&&&&&&private final int&&&&&&&&&&&&&&&&private Grade(int base) {&&&&&&&&&&&&&&&&&&&&&&&&this.base =&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&public abstract int getResult();&&&&&&&&}}结果编译不通过:EnumGrade.java:10: 无法从静态上下文中引用非静态 变量 base&& & & & & & & &&& & & & & & & & & & & ^EnumGrade.java:16: 无法从静态上下文中引用非静态 变量 base&& & & & & & & &return base*2;&& & & & & & & & & & & ^EnumGrade.java:22: 无法从静态上下文中引用非静态 变量 base&& & & & & & & &return base*3;&& & & & & & & & & & & ^于是修改为以下代码:public class EnumGrade {&&&&&&&&public static void main(String[] args) {&&&&&&&&&&&&&&&&for (Grade grade : Grade.values())&&&&&&&&&&&&&&&&&&&&&&&&System.out.println(grade + "'s result is : " + grade.getResult(grade.ordinal()));&&&&&&&&}&&&&&&&&private enum Grade {&&&&&&&&&&&&&&&&A {&&&&&&&&&&&&&&&&&&&&&&&&public int getResult(int base) {&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&return&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&},&&&&&&&&&&&&&&&&B {&&&&&&&&&&&&&&&&&&&&&&&&public int getResult(int base) {&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&return base*2;&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&},&&&&&&&&&&&&&&&&C {&&&&&&&&&&&&&&&&&&&&&&&&public int getResult(int base) {&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&return base*3;&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&};&&&&&&&&&&&&&&&&public abstract int getResult(int base);&&&&&&&&}}运行结果:A's result is : 0
B's result is : 2
C's result is : 6另外Java1.5还有EnumMap和EnumSet(参考):import java.util.EnumMimport java.util.EnumSpublic class EnumState{&&&&&&&&public static void main(String[] args) {&&&&&&&&&&&&&&&&// EnumSet的使用&&&&&&&&&&&&&&&&EnumSet&State& stateSet = EnumSet.allOf(State.class);&&&&&&&&&&&&&&&&for (State s : stateSet) {&&&&&&&&&&&&&&&&&&&&&&&&System.out.println(s);&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&// EnumMap的使用&&&&&&&&&&&&&&&&EnumMap&State,String& stateMap = new EnumMap&State,String&(State.class);&&&&&&&&&&&&&&&&stateMap.put(State.ON, "is On");&&&&&&&&&&&&&&&&stateMap.put(State.OFF, "is off");&&&&&&&&&&&&&&&&for (State s : State.values()) {&&&&&&&&&&&&&&&&&&&&&&&&System.out.println(s.name() + ":" + stateMap.get(s));&&&&&&&&&&&&&&&&}&&&&&&&&}&&&&&&&&private enum State {&&&&&&&&&&&&&&&&ON, OFF&&&&&&&&};}运行结果:ONOFFON:is OnOFF:is off*****因为本人使用javap和jd也得不到更好的反编译代码,某些反编译结果的分析是结合网上的文章和自己看到的反编译结果推理所得。
了这篇文章
类别:┆阅读(0)┆评论(0)

我要回帖

更多关于 c 多态 的文章

 

随机推荐