多态vs覆盖vs重载
在Java方面,当有人问:
什么是多态?
超载或重写是一个可接受的答案?
我觉得还有比这更多的一点。
如果你有一个抽象基类定义了一个没有实现的方法,并且你在子类中定义了这个方法,那么这个方法仍然是重写的吗?
我认为超载并不是正确的答案。
expression多态的最清晰的方法是通过一个抽象的基类(或接口)
public abstract class Human{ ... public abstract void goPee(); }
这个类是抽象的,因为goPee()
方法不能goPee()
定义。 只有男性和女性的子类才可以定义。 另外,人是一个抽象的概念 – 你不能创造一个既不是男性也不是女性的人。 它必须是一个或另一个。
所以我们推迟使用抽象类的实现。
public class Male extends Human{ ... @Override public void goPee(){ System.out.println("Stand Up"); } }
和
public class Female extends Human{ ... @Override public void goPee(){ System.out.println("Sit Down"); } }
现在我们可以告诉整个房间充满人类去撒尿。
public static void main(String[] args){ ArrayList<Human> group = new ArrayList<Human>(); group.add(new Male()); group.add(new Female()); // ... add more... // tell the class to take a pee break for (Human person : group) person.goPee(); }
运行这将产生:
Stand Up Sit Down ...
多态性是一个类实例的行为能力,就好像它是inheritance树中的另一个类的实例,通常是其祖先类之一。 例如,在Java中,所有的类都从Objectinheritance。 因此,您可以创build一个Objecttypes的variables,并为其分配任何类的实例。
重写是一种types的函数,它发生在从另一个类inheritance的类中。 一个覆盖函数“replace”了一个从基类inheritance的函数,但是这样做是为了甚至当它的类的一个实例假装是通过多态的不同types时被调用。 参考前面的例子,你可以定义你自己的类并覆盖toString()函数。 因为这个函数是从Objectinheritance的,所以如果你把这个类的一个实例复制到一个Object-typevariables中,它仍然是可用的。 通常情况下,如果在假装成Object的情况下在类上调用toString(),实际上会触发的toString的版本就是Object自身定义的版本。 但是,由于该函数是一个覆盖,即使类实例的真实types隐藏在多态性后面,也会使用类的toString()定义。
重载是用相同的名字定义多个方法,但是具有不同的参数。 这与压倒性或多态性无关。
下面是伪C#/ Java中的一个多态的例子:
class Animal { abstract string MakeNoise (); } class Cat : Animal { string MakeNoise () { return "Meow"; } } class Dog : Animal { string MakeNoise () { return "Bark"; } } Main () { Animal animal = Zoo.GetAnimal (); Console.WriteLine (animal.MakeNoise ()); }
Main函数不知道动物的types,取决于MakeNoise()方法的特定实现的行为。
编辑:看起来像布赖恩殴打我的一拳。 有趣的是我们使用了相同的例子 但是上面的代码应该有助于澄清这些概念。
多态性意味着多于一种forms,同一个对象根据需要执行不同的操作。
多态性可以通过使用两种方式来实现,那就是
- 方法覆盖
- 方法重载
方法重载意味着使用相同的方法名称在同一个类中写入两个或两个以上的方法,但传递参数是不同的。
方法覆盖意味着我们在不同的类中使用方法名称,这意味着在子类中使用父类方法。
在Java中实现多态的超级类引用variables可以容纳子类对象。
为了实现多态,每个开发人员必须在项目中使用相同的方法名称。
其实两者都是用来实现多态的。
-
您可以在一个类中重写一个或多个子类中的方法。 该方法根据用于实例化对象的类来做不同的事情。
abstract class Beverage { boolean isAcceptableTemperature(); } class Coffee extends Beverage { boolean isAcceptableTemperature() { return temperature > 70; } } class Wine extends Beverage { boolean isAcceptableTemperature() { return temperature < 10; } }
-
你也可以有一个方法,它重载了两个或更多的参数集。 该方法基于传递的参数的types做不同的事情。
class Server { public void pour (Coffee liquid) { new Cup().fillToTopWith(liquid); } public void pour (Wine liquid) { new WineGlass().fillHalfwayWith(liquid); } public void pour (Lemonade liquid, boolean ice) { Glass glass = new Glass(); if (ice) { glass.fillToTopWith(new Ice()); } glass.fillToTopWith(liquid); } }
你是正确的,超载不是答案。
两者都不是最重要的。 覆盖是你得到多态性的手段。 多态性是对象根据其types改变行为的能力。 当展示多态的对象的调用者不知道对象是什么特定types时,这是最好的certificate。
具体说超负荷或超负荷并不能给出完整的画面。 多态性就是一个对象根据其types专门化其行为的能力。
我不同意这里的一些答案,重载是一种多态的forms(参数多态),如果同名的方法可以有不同的行为给予不同的参数types。 一个很好的例子就是运算符重载。 你可以定义“+”来接受不同types的参数 – 比如说string或者int – 并且基于这些types,“+”将会有不同的performance。
多态性还包括inheritance和重写方法,尽pipe它们可以是基本types的抽象或虚拟。 在基于inheritance的多态性方面,Java只支持单个类inheritance,将多态行为限制在单个基类链中。 Java确实支持多接口的实现,这是多态行为的另一种forms。
经典的例子,狗和猫是动物,动物有方法makeNoise。 我可以遍历一个叫makeNoise的动物数组,并期望他们可以在那里做相应的实现。
调用代码不必知道他们是什么特定的动物。
那就是我所认为的多态性。
多态性是对象以多种forms出现的能力。 这涉及到使用inheritance和虚函数来构build可互换的一系列对象。 基类包含虚拟函数的原型,可能未实现,或者按照应用程序的指示使用默认实现,而各种派生类每个实现它们以不同的方式影响不同的行为。
无论是:
重载是当你有相同的函数名称,采用不同的参数。
重写是当一个子类用自己的方法replace父类的方法(这在iteself中不构成多态)。
多态是晚期绑定,例如,基类(父)方法被调用,但直到运行时,应用程序才知道实际的对象是什么 – 它可能是一个子类,其方法是不同的。 这是因为任何子类都可以在定义基类的地方使用。
在Java中,你可以看到集合库的多态性:
int countStuff(List stuff) { return stuff.size(); }
List是基类,如果你计算一个链表,向量,数组或自定义列表的实现,那么编译器就没有任何线索,只要它像List一样:
List myStuff = new MyTotallyAwesomeList(); int result = countStuff(myStuff);
如果你超载,你会有:
int countStuff(LinkedList stuff) {...} int countStuff(ArrayList stuff) {...} int countStuff(MyTotallyAwesomeList stuff) {...} etc...
并且编译器会select正确版本的countStuff()来匹配参数。
多态只是意味着“多种forms”。
它并不要求inheritance来实现…作为接口实现,它根本不是inheritance,它服务于多态的需求。 可以说接口的实现为多态的需求提供了比inheritance更好的“更好”的需求。
例如,你会创build一个超级类来描述所有可以飞行的东西吗? 我不该想。 你将被最好地创build一个界面,描述飞行,并离开它。
因此,由于接口描述行为,而方法名称描述行为(对程序员),所以将方法重载看作较lessforms的多态性并不算什么。
术语重载是指具有相同名称的多个版本的东西,通常是具有不同参数列表的方法
public int DoSomething(int objectId) { ... } public int DoSomething(string objectName) { ... }
所以这些函数可能会做同样的事情,但是您可以select使用ID或名称来调用它。 与inheritance,抽象类等无关
重写通常是指多态,就像你在你的问题中所描述的那样
什么是多态?
从java 教程
字典中的多态性定义是指生物学中的一个原理,其中生物或物种可以有许多不同的forms或阶段。 这个原理也可以应用于面向对象的编程和像Java语言这样的语言。 类的子类可以定义自己的唯一行为,但是可以共享父类的一些相同的function。
通过考虑实例和定义, 重写应该被接受的答案。
关于你的第二个查询:
如果你有一个抽象基类定义了一个没有实现的方法,并且你在子类中定义了这个方法,那么这个方法仍然是重写的吗?
它应该被称为覆盖。
看看这个例子来了解不同types的重写。
- 基类没有提供实现,子类必须覆盖完整的方法 – (抽象)
- 基类提供默认实现,子类可以改变行为
- 子类通过调用
super.methodName()
作为第一条语句来向基类实现添加扩展 - 基类定义了algorithm的结构(模板方法),子类将覆盖algorithm的一部分
代码片段:
import java.util.HashMap; abstract class Game implements Runnable{ protected boolean runGame = true; protected Player player1 = null; protected Player player2 = null; protected Player currentPlayer = null; public Game(){ player1 = new Player("Player 1"); player2 = new Player("Player 2"); currentPlayer = player1; initializeGame(); } /* Type 1: Let subclass define own implementation. Base class defines abstract method to force sub-classes to define implementation */ protected abstract void initializeGame(); /* Type 2: Sub-class can change the behaviour. If not, base class behaviour is applicable */ protected void logTimeBetweenMoves(Player player){ System.out.println("Base class: Move Duration: player.PlayerActTime - player.MoveShownTime"); } /* Type 3: Base class provides implementation. Sub-class can enhance base class implementation by calling super.methodName() in first line of the child class method and specific implementation later */ protected void logGameStatistics(){ System.out.println("Base class: logGameStatistics:"); } /* Type 4: Template method: Structure of base class can't be changed but sub-class can some part of behaviour */ protected void runGame() throws Exception{ System.out.println("Base class: Defining the flow for Game:"); while ( runGame) { /* 1. Set current player 2. Get Player Move */ validatePlayerMove(currentPlayer); logTimeBetweenMoves(currentPlayer); Thread.sleep(500); setNextPlayer(); } logGameStatistics(); } /* sub-part of the template method, which define child class behaviour */ protected abstract void validatePlayerMove(Player p); protected void setRunGame(boolean status){ this.runGame = status; } public void setCurrentPlayer(Player p){ this.currentPlayer = p; } public void setNextPlayer(){ if ( currentPlayer == player1) { currentPlayer = player2; }else{ currentPlayer = player1; } } public void run(){ try{ runGame(); }catch(Exception err){ err.printStackTrace(); } } } class Player{ String name; Player(String name){ this.name = name; } public String getName(){ return name; } } /* Concrete Game implementation */ class Chess extends Game{ public Chess(){ super(); } public void initializeGame(){ System.out.println("Child class: Initialized Chess game"); } protected void validatePlayerMove(Player p){ System.out.println("Child class: Validate Chess move:"+p.getName()); } protected void logGameStatistics(){ super.logGameStatistics(); System.out.println("Child class: Add Chess specific logGameStatistics:"); } } class TicTacToe extends Game{ public TicTacToe(){ super(); } public void initializeGame(){ System.out.println("Child class: Initialized TicTacToe game"); } protected void validatePlayerMove(Player p){ System.out.println("Child class: Validate TicTacToe move:"+p.getName()); } } public class Polymorphism{ public static void main(String args[]){ try{ Game game = new Chess(); Thread t1 = new Thread(game); t1.start(); Thread.sleep(1000); game.setRunGame(false); Thread.sleep(1000); game = new TicTacToe(); Thread t2 = new Thread(game); t2.start(); Thread.sleep(1000); game.setRunGame(false); }catch(Exception err){ err.printStackTrace(); } } }
输出:
Child class: Initialized Chess game Base class: Defining the flow for Game: Child class: Validate Chess move:Player 1 Base class: Move Duration: player.PlayerActTime - player.MoveShownTime Child class: Validate Chess move:Player 2 Base class: Move Duration: player.PlayerActTime - player.MoveShownTime Base class: logGameStatistics: Child class: Add Chess specific logGameStatistics: Child class: Initialized TicTacToe game Base class: Defining the flow for Game: Child class: Validate TicTacToe move:Player 1 Base class: Move Duration: player.PlayerActTime - player.MoveShownTime Child class: Validate TicTacToe move:Player 2 Base class: Move Duration: player.PlayerActTime - player.MoveShownTime Base class: logGameStatistics:
重写更像是通过声明与上层方法(super方法)具有相同名称和签名的方法来隐藏inheritance的方法,这会为类添加多态行为。 换句话说,决定select什么级别的方法将在运行时进行,而不是在编译时。 这导致了接口和实现的概念。
当我们inheritance基类和派生类时重写,那么如果在派生类中有一个方法与方法(相同名称,相同的参数,相同的返回types)相同的名称在基类中定义,那么它的称为覆盖..
class Vehicle{ void run() { System.out.println("Vehicle is running"); } } class Bike2 extends Vehicle{ void run() { System.out.println("Bike is running safely"); } public static void main(String args[]){ Bike2 obj = new Bike2(); obj.run(); }
输出:自行车正在安全运行……..要更清楚地了解覆盖,请访问: http : //javabyroopam.blogspot.in/
重载只是两个名称相同,但参数列表不同的方法称为重载。
class Calculation{ void sum(int a,int b){System.out.println(a+b);} void sum(int a,int b,int c){System.out.println(a+b+c);} public static void main(String args[]){ Calculation obj=new Calculation(); obj.sum(10,10,10); obj.sum(20,20); } }
输出30,20
当你定义2个名字相同但参数不同的方法时,重载就是这样
重写是通过一个子类中具有相同名称的函数来改变基类的行为的地方。
所以多态性与重写有关,但并不是真正的重载。
但是,如果有人给我一个简单的答案:“什么是多态? 我会要求进一步的解释。
多态性更有可能就其意义而言…在Java中的覆盖
这是关于在不同情况下SAME对象的不同行为(在编程方式…你可以调用不同的自variables)
我想下面的例子将帮助你理解…虽然它不是纯粹的Java代码…
public void See(Friend) { System.out.println("Talk"); }
但如果我们改变了ARGUMENT …行为将会改变…
public void See(Enemy) { System.out.println("Run"); }
人(这里的“对象”)是一样的…
多态性是一个对象的多重实现,或者你可以说多个forms的对象。 可以说你有类Animals
作为抽象基类,它有一个方法叫做movement()
,它定义了动物移动的方式。 现在我们有了不同种类的动物,它们的运动也不同,其中一些有两条腿,另一些有四条腿,一些没有腿等。为了定义地球上每只动物的不同movement()
,我们需要应用多态性。 然而,你需要定义更多的类,比如Dogs
Cats
Fish
等等。然后,你需要从基类Animal中扩展这些类,并且根据你拥有的每个动物,用一个新的移动function覆盖它的方法movement()
。 你也可以使用Interfaces
来实现。 这里的关键字是重写,重载是不同的,不被视为多态。 通过重载,可以定义多个“同名”方法,但在同一对象或类上具有不同的参数。
我认为你们正在混合的概念。 多态性是一个对象在运行时performance不同的能力。 为了实现这一点,你需要两个必要条件:
- 晚绑定
- 遗产。
话虽如此, 重载意味着不同的重写,取决于你正在使用的语言。 例如在Java中不存在覆盖,但重载 。 子类中提供了对其基类具有不同签名的重载方法。 否则,他们将被覆盖 (请看,我的意思是现在的事实是没有办法从对象之外调用你的基类方法)。
但是在C ++中并非如此。 任何重载的方法,无论签名是否相同(不同的数量,不同的types),都被重写 。 那就是到目前为止,很显然,从子类对象外部调用基类的方法在子类中不再可用。
所以答案就是在谈论Java使用重载时 。 在任何其他语言可能会有所不同,因为它发生在c + +
多态性涉及语言使用单一接口统一处理不同对象的能力; 因此它与覆盖有关,所以接口(或基类)是多态的,实现者是覆盖的对象(同一个奖牌的两个面)
无论如何,这两个术语之间的区别是使用其他语言(如c ++)更好地解释:如果基本函数是虚拟的,则c ++中的多态对象的行为与Java对象相同,但如果该方法不是虚拟的,则代码跳转是静态parsing的,并且真正的types在运行时不被检查,所以多态性包括一个对象根据访问它所用的接口行为不同的能力; 让我用伪代码来做一个例子:
class animal { public void makeRumor(){ print("thump"); } } class dog extends animal { public void makeRumor(){ print("woff"); } } animal a = new dog(); dog b = new dog(); a.makeRumor() -> prints thump b.makeRumor() -> prints woff
(假设makeRumor不是虚拟的)
Java并没有真正提供这种级别的多态(也称为对象切片)。
动物a =新狗(); 狗b =新狗();
a.makeRumor() -> prints thump b.makeRumor() -> prints woff
在这两种情况下,它只会打印woff ..因为a和b是指类狗
import java.io.IOException; class Super { protected Super getClassName(Super s) throws IOException { System.out.println(this.getClass().getSimpleName() + " - I'm parent"); return null; } } class SubOne extends Super { @Override protected Super getClassName(Super s) { System.out.println(this.getClass().getSimpleName() + " - I'm Perfect Overriding"); return null; } } class SubTwo extends Super { @Override protected Super getClassName(Super s) throws NullPointerException { System.out.println(this.getClass().getSimpleName() + " - I'm Overriding and Throwing Runtime Exception"); return null; } } class SubThree extends Super { @Override protected SubThree getClassName(Super s) { System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and Returning SubClass Type"); return null; } } class SubFour extends Super { @Override protected Super getClassName(Super s) throws IOException { System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and Throwing Narrower Exception "); return null; } } class SubFive extends Super { @Override public Super getClassName(Super s) { System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and have broader Access "); return null; } } class SubSix extends Super { public Super getClassName(Super s, String ol) { System.out.println(this.getClass().getSimpleName()+ " - I'm Perfect Overloading "); return null; } } class SubSeven extends Super { public Super getClassName(SubSeven s) { System.out.println(this.getClass().getSimpleName()+ " - I'm Perfect Overloading because Method signature (Argument) changed."); return null; } } public class Test{ public static void main(String[] args) throws Exception { System.out.println("Overriding\n"); Super s1 = new SubOne(); s1.getClassName(null); Super s2 = new SubTwo(); s2.getClassName(null); Super s3 = new SubThree(); s3.getClassName(null); Super s4 = new SubFour(); s4.getClassName(null); Super s5 = new SubFive(); s5.getClassName(null); System.out.println("Overloading\n"); SubSix s6 = new SubSix(); s6.getClassName(null, null); s6 = new SubSix(); s6.getClassName(null); SubSeven s7 = new SubSeven(); s7.getClassName(s7); s7 = new SubSeven(); s7.getClassName(new Super()); } }