Java Language
Полиморфизм
Поиск…
Вступление
Полиморфизм является одним из основных концепций ООП (объектно-ориентированного программирования). Слово полиморфизма было получено из греческих слов «poly» и «morphs». Поли означает «много», а морфы означают «формы» (многие формы).
Существует два способа выполнения полиморфизма. Перегрузка метода и переопределение метода .
замечания
Interfaces
- это еще один способ добиться полиморфизма в Java, помимо классового наследования. Интерфейсы определяют список методов, которые формируют API программы. Классы должны implement
interface
, переопределяя все его методы.
Перегрузка метода
Перегрузка метода , также известная как перегрузка функций , - это способность класса иметь несколько методов с тем же именем, если они отличаются друг от друга по количеству или типу аргументов.
Компилятор проверяет подпись метода для перегрузки метода.
Подпись метода состоит из трех вещей:
- Имя метода
- Количество параметров
- Типы параметров
Если эти три одинаковы для любых двух методов в классе, то компилятор выдает ошибку повторяющегося метода .
Этот тип полиморфизма называется статическим или компиляционным полиморфизмом, потому что соответствующий метод, который должен быть вызван, определяется компилятором во время компиляции на основе списка аргументов.
class Polymorph {
public int add(int a, int b){
return a + b;
}
public int add(int a, int b, int c){
return a + b + c;
}
public float add(float a, float b){
return a + b;
}
public static void main(String... args){
Polymorph poly = new Polymorph();
int a = 1, b = 2, c = 3;
float d = 1.5, e = 2.5;
System.out.println(poly.add(a, b));
System.out.println(poly.add(a, b, c));
System.out.println(poly.add(d, e));
}
}
Это приведет к:
2
6
4.000000
Перегруженные методы могут быть статическими или нестационарными. Это также не приводит к перегрузке метода.
public class Polymorph {
private static void methodOverloaded()
{
//No argument, private static method
}
private int methodOverloaded(int i)
{
//One argument private non-static method
return i;
}
static int methodOverloaded(double d)
{
//static Method
return 0;
}
public void methodOverloaded(int i, double d)
{
//Public non-static Method
}
}
Также, если вы измените тип возвращаемого метода, мы не сможем получить его как перегрузку метода.
public class Polymorph {
void methodOverloaded(){
//No argument and No return type
}
int methodOverloaded(){
//No argument and int return type
return 0;
}
Переопределение метода
Переопределение метода - это способность подтипов переопределять (переопределять) поведение их супертипов.
В Java это переводит в подклассы, переопределяя методы, определенные в суперклассе. В Java все непримитивные переменные являются фактически references
, которые схожи с указателями на местоположение фактического объекта в памяти. references
имеют только один тип, который является типом, с которым они были объявлены. Однако они могут указывать на объект либо их объявленного типа, либо любого из его подтипов.
Когда метод вызывается по reference
, вызывается соответствующий метод фактического объекта, на который указывают .
class SuperType {
public void sayHello(){
System.out.println("Hello from SuperType");
}
public void sayBye(){
System.out.println("Bye from SuperType");
}
}
class SubType extends SuperType {
// override the superclass method
public void sayHello(){
System.out.println("Hello from SubType");
}
}
class Test {
public static void main(String... args){
SuperType superType = new SuperType();
superType.sayHello(); // -> Hello from SuperType
// make the reference point to an object of the subclass
superType = new SubType();
// behaviour is governed by the object, not by the reference
superType.sayHello(); // -> Hello from SubType
// non-overridden method is simply inherited
superType.sayBye(); // -> Bye from SuperType
}
}
Правила, которые следует учитывать
Чтобы переопределить метод в подклассе, метод переопределения (т. Е. Один в подклассе) ДОЛЖЕН ИМЕТЬ :
- то же имя
- тот же тип возврата в случае примитивов (подкласс разрешен для классов, это также называется ковариантными типами возврата).
- тот же тип и порядок параметров
- он может вызывать только те исключения, которые объявлены в предложении throws метода суперкласса или исключениях, которые являются подклассами объявленных исключений. Он также может выбрать НЕ выбрасывать какие-либо исключения. Имена типов параметров не имеют значения. Например, void methodX (int i) аналогичен void methodX (int k)
- Мы не можем переопределить конечные или статические методы. Единственное, что мы можем сделать, это изменить только тело метода.
Добавление поведения путем добавления классов без касания существующего кода
import java.util.ArrayList;
import java.util.List;
import static java.lang.System.out;
public class PolymorphismDemo {
public static void main(String[] args) {
List<FlyingMachine> machines = new ArrayList<FlyingMachine>();
machines.add(new FlyingMachine());
machines.add(new Jet());
machines.add(new Helicopter());
machines.add(new Jet());
new MakeThingsFly().letTheMachinesFly(machines);
}
}
class MakeThingsFly {
public void letTheMachinesFly(List<FlyingMachine> flyingMachines) {
for (FlyingMachine flyingMachine : flyingMachines) {
flyingMachine.fly();
}
}
}
class FlyingMachine {
public void fly() {
out.println("No implementation");
}
}
class Jet extends FlyingMachine {
@Override
public void fly() {
out.println("Start, taxi, fly");
}
public void bombardment() {
out.println("Fire missile");
}
}
class Helicopter extends FlyingMachine {
@Override
public void fly() {
out.println("Start vertically, hover, fly");
}
}
объяснение
a) Класс MakeThingsFly
может работать со всем, что относится к типу FlyingMachine
.
b) Метод letTheMachinesFly
также работает без каких-либо изменений (!) при добавлении нового класса, например PropellerPlane
:
public void letTheMachinesFly(List<FlyingMachine> flyingMachines) {
for (FlyingMachine flyingMachine : flyingMachines) {
flyingMachine.fly();
}
}
}
Это сила полиморфизма. Вы можете реализовать с ним открытый-закрытый принцип .
Виртуальные функции
Виртуальные методы - это методы в Java, которые нестатические и без ключевого слова Final впереди. Все методы по умолчанию являются виртуальными в Java. Виртуальные методы играют важную роль в полиморфизме, потому что классы детей в Java могут переопределять методы своих родительских классов, если переопределенная функция нестатическая и имеет одну и ту же подпись метода.
Однако существуют некоторые методы, которые не являются виртуальными. Например, если метод объявлен private или с ключевым словом final, то метод не является виртуальным.
Рассмотрим следующий измененный пример наследования с Virtual Methods из этого сообщения StackOverflow. Как виртуальные функции работают на C # и Java? :
public class A{
public void hello(){
System.out.println("Hello");
}
public void boo(){
System.out.println("Say boo");
}
}
public class B extends A{
public void hello(){
System.out.println("No");
}
public void boo(){
System.out.println("Say haha");
}
}
Если мы вызываем класс B и вызываем hello () и boo (), мы получим «No» и «Say haha» в качестве выходного результата, потому что B переопределяет те же методы из A. Хотя приведенный выше пример почти такой же, как метод переопределения, важно понимать, что методы в классе A - это все по умолчанию Virtual.
Кроме того, мы можем реализовать виртуальные методы, используя ключевое слово abstract. Методы, объявленные с ключевым словом «abstract», не имеют определения метода, то есть тело метода еще не реализовано. Рассмотрим пример сверху, за исключением того, что метод boo () объявлен абстрактным:
public class A{
public void hello(){
System.out.println("Hello");
}
abstract void boo();
}
public class B extends A{
public void hello(){
System.out.println("No");
}
public void boo(){
System.out.println("Say haha");
}
}
Если мы будем вызывать boo () из B, выход будет по-прежнему «Say haha», так как B наследует абстрактный метод boo () и делает вывод boo () «Say haha».
Используемые источники и дальнейшие чтения:
Как виртуальные функции работают на C # и Java?
Ознакомьтесь с этим замечательным ответом, в котором содержится более полная информация о виртуальных функциях:
Можете ли вы писать виртуальные функции / методы на Java?
Полиморфизм и различные типы переопределения
Из учебника java
Словариское определение полиморфизма относится к принципу в биологии, в котором организм или вид могут иметь много разных форм или стадий. Этот принцип также может быть применен к объектно-ориентированному программированию и языкам, таким как язык Java. Подклассы класса могут определять свое собственное уникальное поведение и совместно использовать одни и те же функциональные возможности родительского класса.
Взгляните на этот пример, чтобы понять различные типы переопределения.
- Базовый класс не обеспечивает реализацию, а подкласс должен переопределить полный метод - (аннотация)
- Базовый класс обеспечивает реализацию по умолчанию, а подкласс может изменять поведение
-
super.methodName()
добавляет расширение к реализации базового класса, вызываяsuper.methodName()
как первый оператор - Базовый класс определяет структуру алгоритма (метод Template), а подкласс будет переопределять часть алгоритма
фрагмент кода:
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: