Java — это язык объектно-ориентированного программирования (ООП), который особенно полезен, когда вам нужно реализовать хорошо структурированный код и повысить его повторное использование и ремонтопригодность.
ООП использует четыре принципа для описания отношений между классами: наследование, инкапсуляция, абстракция и полиморфизм. В каждой Java-программе есть как минимум один класс. Поэтому очень важно понимать принципы ООП и то, как их использовать в своих приложениях.
В этой статье рассматривается полиморфизм в его различных формах, как он соотносится с другими классами Java, а также варианты его использования и передовой опыт.
Если у вас мало времени, воспользуйтесь приведенными ниже ссылками, чтобы найти именно то, что вы ищете:
Table of Contents
ToggleПолиморфизм, что буквально означает «разные формы», является одной из основных концепций ООП. Полиморфизм исследует, как создать и использовать два метода с одинаковыми именами для выполнения двух разных функций — например, добавить две функции с одинаковыми именами, но принимающие разные параметры.
Например, давайте рассмотрим, как можно определить две функции с именем Multiply (). Один используется для вычисления произведения двух целых чисел, а другой — для вычисления произведения двух чисел типа double.
public class Main {
public static void main(String[] args) {
// using the first method
Multiplier.Multiply(3,5);
// using the second method
Multiplier.Multiply(3.5,5.1);
}
}
Когда код запущен, вывод выглядит следующим образом:
Целочисленное умножение, результат = 15
двойное умножение, результат = 17,849999999999998
Вы заметите, что при вызове двух методов нет никакой разницы, кроме передаваемых параметров. Кроме того, обратите внимание, что выходные данные зависят от типа переданных параметров.
Понятно, что разработчики иногда путают полиморфизм с другой ключевой концепцией ООП: наследованием. Однако эти два принципа существенно различаются.
При наследовании класс, который называется дочерним классом, может наследовать методы и атрибуты другого класса (родительского класса). См. следующий пример:
class Shape {
// methods of Shape class
}
class Square extends Shape {
// methods of Shape class are not specified here but are automatically accessible to this Square class thanks to inheritance
// methods of Square class
}
При наследовании без полиморфизма дочерний класс наследует те же атрибуты и методы родительского класса без каких-либо модификаций их функциональности. При полиморфизме дочерний класс наследует атрибуты и методы, но предоставляет собственную реализацию (код) для этих методов.
Наследование — это способ обеспечить возможность повторного использования кода, а полиморфизм — это способ динамического определения того, какая версия функции будет вызываться. Например, наследование позволяет использовать атрибуты и методы родительского класса. Напротив, полиморфизм позволяет дочернему классу определять свою версию функции с тем же именем, что и в родительском классе. В качестве альтернативы он позволяет реализовать функцию с одним и тем же именем несколькими различными способами и выбирает один из них для выполнения на основе количества и типов заданных параметров.
Полиморфизм также используется для поддержки принципа «открыто-закрыто», который представляет собой букву «О» в аббревиатуре SOLID. В нем говорится, что код должен быть открыт для расширения и закрыт для модификации. Другими словами, когда вы пытаетесь добавить больше функциональности в свой код, вам не следует изменять существующие классы (закрытые для модификации). Вместо этого следует расширять функциональность за счет наследования и полиморфизма.
Если у вас есть два типа автомобилей, например BMW и Mercedes, вы можете создать класс Car, а затем создать класс BMW и класс Mercedes, каждый из которых наследуется от класса Car. Это означает, что они наследуют его атрибуты и функции.
При полиморфизме можно предположить, что Car (родительский класс) может быть BMW (дочерний класс) или Mercedes (дочерний класс). Следовательно, вы можете использовать родительский класс Car для ссылки на любой из двух типов автомобилей.
public class Car{}
Public class BMW extends Car {}
Public class Mercedes extends Car{}
Car myCar = new BMW();
//…some code…
myCar = new Mercedes();
//…some code…
В этом примере кода определяется переменная myCar типа Car и используется для хранения нового объекта — BMW. Затем он использовал ту же переменную для ссылки на Mercedes, что возможно, потому что они оба являются дочерними классами Car.
Это простой пример повышения приведения, когда родительский класс ссылается на объект из дочернего класса. Используя восходящее преобразование, вы можете получить доступ к дочерней версии родительского класса. Это означает, что вы можете получить доступ к методам, которые были определены исключительно в родительском классе, но если метод с тем же именем указан в дочернем классе, вы можете получить доступ к этому. Восходящее преобразование полезно, когда вашему приложению необходимо определить, какую версию кода вызывать во время выполнения.
Java поддерживает два типа полиморфизма:
Этот тип полиморфизма, также называемый статическим полиморфизмом, достигается путем создания нескольких методов с одинаковыми именами в одном классе, но каждый из которых имеет разное количество параметров или параметров разных типов данных.
Обратите внимание, что в приведенном ниже примере три метода имеют одно и то же имя, но каждый из них принимает разное количество параметров или разные типы данных:
package com.hubspot;
class Multiplier {
static void Multiply(int a, int b)
{
System.out.println("Integer Multiplication, Result = "+ a*b);
}
// Method 2
static void Multiply(double a, double b)
{
System.out.println("double Multiplication, Result = "+ a*b);
}
// Method 3
static void Multiply(double a, double b, double c)
{
System.out.println("Three parameters, double Multiplication, Result = "+ a*b*c);
}
}
public class Main {
public static void main(String[] args) {
// using the first method
Multiplier.Multiply(3,5);
// using the second method
Multiplier.Multiply(3.5,5.1);
// using the third method
Multiplier.Multiply(3.6,5.2, 6.3);
}
}
Когда код запущен, вывод выглядит следующим образом:
Целочисленное умножение, результат = 15
двойное умножение, результат = 17,849999999999998
Три параметра, двойное умножение, результат = 117,936
Первый метод принимает два целых числа с разными параметрами, второй метод принимает два параметра типа double, а третий метод принимает три параметра типа double. Обратите внимание, что мы не указываем никакой дополнительной информации при их вызове. Эти три вызова одинаковы, за исключением номера или типа параметра.
Этот тип полиморфизма, также называемый динамическим полиморфизмом, возникает, когда дочерний класс имеет собственное определение одного из методов-членов родительского класса. Это называется переопределением метода. В большинстве случаев полиморфизм среды выполнения связан с повышением приведения типов. Это когда родительский класс указывает на экземпляр дочернего класса.
Вот пример:
package com.hupspot;
// Super Class
class Shape{
protected double length;
Shape(double length){
this.length = length;
}
void area(){
}
}
// Child class
class Square extends Shape{
//constructor
Square(double side){
super(side); // calling the super class constructor
}
//Overriding area() method
void area(){
System.out.println("Square Area = " + length*length);
}
}
// Child class
class Circle extends Shape{
//constructor
Circle(double radius){
super(radius); // calling the super class constructor
}
//Overriding area() method
void area(){
System.out.println("Circle Area = " + 3.14*length*length);;
}
}
public class Main {
public static void main(String[] args){
Shape shape = new Square(5.0);
// calling the area() method of the Square Class
shape.area();
shape = new Circle(5.0); // upcasting
// calling the area() method of the Circle Class
shape.area();
}
}
Выход:
Квадратная площадь = 25,0
Площадь круга = 75,0
Последний пример представляет принцип SOLID «открыто-закрыто». Мы расширили код дочерними классами и не изменили существующий класс Shape.
Одним из вариантов использования полиморфизма является использование одного имени метода для автоматического вызова правильного метода в зависимости от класса. Например, при использовании одного элемента хранилища для хранения нескольких типов в следующем примере реализуется Set типа Shape из предыдущего примера и сохраняются объекты из разных дочерних классов, таких как Circle s и Square s:
Set<Shape> hs = new HashSet<Shape>();
hs.add(circle1);
hs.add(circle2);
hs.add(square1);
hs.add(square2);
for(Shape hs_element : hs){
hs_element.area();
}
Другой пример использования полиморфизма — замена условных операторов в вашем коде. Например, в приведенном ниже коде используется оператор switch в методе области, чтобы определить, какой код запускать при его вызове, на основе первого переданного ему параметра. Вы заметите, что он достигает того же результата, что и приведенный выше пример кода полиморфизма времени выполнения. Однако этот подход не так прост.
package org.hubspot;
// Super Class
class Shape{
enum Type {
SQUARE,
CIRCLE
}
protected double length;
Type shape_type;
Shape(Type shape, double length){
this.length = length;
this.shape_type = shape;
}
double area(){
double area = 0;
switch (shape_type)
{
case SQUARE:
area = length * length;
break;
case CIRCLE:
area = 3.14*length * length;
break;
}
return area;
}
}
public class Main {
public static void main(String[] args) {
Shape Circle = new Shape(Shape.Type.CIRCLE, 10);
System.out.println("Circle Area equals = "+ Circle.area());
Shape Square = new Shape(Shape.Type.SQUARE, 10);
System.out.println("Square Area equals = "+ Square.area());
}
Полиморфизм — удобный инструмент при разработке на Java или любом другом объектно-ориентированном языке. В этой статье объясняется, что такое полиморфизм в Java и как его использовать в ваших приложениях. Он показал примеры использования полиморфизма, такие как множитель, класс и различные вычисления площади для разных форм.
Мы также определили некоторые преимущества полиморфизма, такие как возможность повторного использования кода. Кроме того, он позволяет использовать единый интерфейс для обработки различных типов классов. Он также придерживается принципа SOLID ООП, расширяя классы вместо использования случая переключения. В результате приложение открыто для расширения и закрыто для модификации.
Наконец, полиморфизм очень полезен при разрешении методов, вызываемых либо во время выполнения, либо во время компиляции.