Переопределение-перегрузка функции в Java
В чем разница между переопределением и перегрузкой?
6 ответов
Перегрузка: выбор сигнатуры метода во время компиляции на основе количества и типа указанных аргументов.
Переопределение: выбор реализации метода во время выполнения на основе фактического типа целевого объекта (в отличие от типа выражения во время компиляции)
Например:
class Base
{
void foo(int x)
{
System.out.println("Base.foo(int)");
}
void foo(double d)
{
System.out.println("Base.foo(double)");
}
}
class Child extends Base
{
@Override void foo (int x)
{
System.out.println("Child.foo(int)");
}
}
...
Base b = new Child();
b.foo(10); // Prints Child.foo(int)
b.foo(5.0); // Prints Base.foo(double)
Оба вызова являются примерами перегрузки. Есть два метода, называемых foo
и компилятор определяет, какую подпись вызывать.
Первый вызов является примером переопределения. Компилятор выбирает сигнатуру "foo(int)", но затем во время выполнения тип целевого объекта определяет, что используемая реализация должна быть Child
,
Перегрузка методов - это хитрость компилятора, позволяющая использовать одно и то же имя для выполнения различных действий в зависимости от параметров.
Переопределение метода означает, что вся его функциональность заменяется. Переопределение - это то, что делается в дочернем классе для метода, определенного в родительском классе.
Перегрузка:
public Bar foo(int some);
public Bar foo(int some, boolean x); // Same method name, different signature.
Переопределение:
public Bar foo(int some); // Defined in some class A
public Bar foo(int some); // Same method name and signature. Defined in subclass of A.
Если бы второй метод не был определен, он унаследовал бы первый метод. Теперь он будет заменен вторым методом в подклассе A.
Перегрузка - похожая подпись - то же имя, разные параметры
void foo() {
/** overload */
}
void foo( int a ) {
/** overload */
}
int foo() {
/** this is NOT overloading, signature is for compiler SAME like void foo() */
}
Переопределить - вы можете переопределить тело метода, когда наследуете его.
class A {
void foo() {
/** definition A */
}
}
class B extends A {
void foo() {
/** definition B, this definition will be used when you have instance of B */
}
}
На интересную вещь упомянуть:
public static doSomething(Collection<?> c) {
// do something
}
public static doSomething(ArrayList<?> l) {
// do something
}
public static void main(String[] args) {
Collection<String> c = new ArrayList<String> ();
doSomething(c); // which method get's called?
}
Можно предположить, что метод с аргументом ArrayList будет вызван, но это не так. Первый метод вызывается, так как правильный метод выбран во время компиляции.
Override
Когда метод, который наследуется подклассом от суперкласса, заменяется (переопределяется) в подклассе.
class A {
void foo() {
/** definition A of foo */
}
}
class B extends A {
void foo() {
/** definition B of foo */
}
}
Теперь, если вы позвоните foo
с помощью:
A a = new B();
a.foo();
B
значение foo
будет работать Это не так интуитивно понятно, так как вы получите ошибку компиляции, если класс A
не было метода с именем foo
, Итак, тип объекта a
который A
должен иметь метод foo
, то вы можете вызвать его, и метод foo
будет выполнен экземпляр, который является классом B
отсюда "время исполнения".
перегрузка
При создании метода с тем же именем, что и существующий метод. Чтобы избежать ошибки времени компиляции, вы должны определить новый метод с параметрами, отличными от существующего. Таким образом, методы будут различимы. Имейте метод с тем же именем и параметрами, но другой тип возвращаемого значения все еще неопределенный и, следовательно, приведет к ошибке компиляции. Пример перегрузки:
class A {
void bar(int i) {}
// The following method is overloading the method bar
void bar(Object a) {}
// The following will cause a compile error.
// Parameters should differ for valid overload
boolean bar(int i) {
return true;
}
}