class ClassA {
String whoAmI() {
return "ClassA";
}
}
class ClassB extends ClassA{
String whoAmI() {
return "ClassB";
}
}
class Main {
public static void main(String[] args) {
ClassA obj1 = new ClassA();
ClassA obj2 = new ClassB();
System.out.println(obj1.whoAmI());
System.out.println(obj2.whoAmI());
}
}
Вывод кода выше:
ClassA
ClassB
В вышеприведенном коде вывод выглядит так, как ожидается, что я могу увидеть методы класса "ClassB", когда я создаю ссылочную переменную класса "ClassA" и создаю экземпляр с помощью "new ClassB();", но почему это не так в случае когда я создаю ссылочную переменную интерфейса "Список" и создаю ее, используя свой класс реализации "ArrayList" (я знаю, что мы не можем напрямую создавать объекты интерфейсов)?
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List objList = new ArrayList();
objList.add("One");
objList.add("Two");
objList.add("Three");
System.out.println(objList.get(1)); // Only able to call methods of interface "List", but not methods of its implementing class "ArrayList. WHY?"
}
}
Если класс "ArrayList" реализует интерфейс "Список", то почему я не могу вызывать методы класса "ArrayList"?
Я знаю, что в моем сознании есть некоторое недоразумение о концепции полиморфизма. Пожалуйста, помогите мне разобраться в этом недоразумении!
полиморфизм имеет два понятия о методах.
перегрузка метода - где вы перегружаете/добавляете/имеете разные параметры в своем методе
например:
class ClassA {
String whoAmI() {
return "ClassA";
}
}
class ClassB extends ClassA{
// you are overloading the method whoAmI from class A
String whoAmI(String name) {
return "ClassB "+name;
}
}
метод overriding - где вы переопределяете/изменяете функцию по умолчанию метода
class ClassA {
String whoAmI() {
return "ClassA";
}
}
class ClassB extends ClassA{
// override whoAmI method change function
String whoAmI() {
//do something
int foo = 5 + 10;
return "ClassB foo: "+foo;
}
}
и в вашем вопросе
class ClassA {
String whoAmI() {
return "ClassA";
}
}
class Main {
public static void main(String[] args) {
Object obj1 = new ClassA();
obj1.whoAmI();
}
}
от вашего фрагмента кода выше вы ссылаетесь объект ClassA()
в качестве Object
которого объект does'nt имеет whoAmI()
метод. вы говорите, что obj1
- это тип объекта ClassA(), который имеет разные наборы методов.
Object
/
ClassA()
|
method:whoAmI()
ссылаясь на иерархию классов выше. classA()
имеет метод whoAmI()
и Object
имеет equals(), hashCode(), getClass(), etc...()
когда вы расширяетесь до Object
super class
не наследует методы внутри child class
. В вашем примере, поскольку ClassA()
расширяет Object
(суперкласс), Object
не будет наследовать метод whoAmI()
но это наоборот. ClassA()
наследует методы внутри Object
. и если вы ссылаетесь на свой ClassA()
на Object
и получаете доступ к методу внутри ClassA()
вам нужно ClassA()
свой Object
в ClassA()
чтобы сообщить компилятору, что я ClassA()
метод из ClassA()
.
Редакция:
в вашем примере вывод не является тем же, поскольку вы указываете ссылку ClassB()
на ClassA()
которая переопределяет метод whoAmI();
и в вашем примере List
ArrayList
реализует List
но переменная экземпляра - A List
. см. "диаграмма ниже"
Пользовательский класс
Class A Class B extends A
method: whoAmI() method override: whoAmI()
output: "i am class A" output: "i am new class B"
method: fooMethod()
output: "my own method"
поэтому, когда вы объявляете объект Class A
вы будете использовать класс методы, поля и т.д.
ClassA obj = new ClassA();
после того, как вы измените ссылочный объект на ClassB()
вы теперь будете использовать метод переопределения ClassB's
/различные наборы полей и т.д., но это не означает, что вы можете вызвать fooMethod()
легко, потому что помните, что это все еще ClassA
методы в ClassB
внутри ClassA
будет переопределен.
ClassA obj = new ClassB();
// override whoAmI() method changed output
но когда вы бросаете obj в ClassB()
это похоже на то, что вы сообщаете компилятору, что это obj является ClassB()
когда вы можете использовать fooMethod()
потому что компилятор считает, что obj(casted)
является ClassB
((ClassB)obj).fooMethod();
Я попытался наилучшим образом, чтобы объяснить это самым простым способом, который я знаю. cheerioo
Object obj1 = new ClassA();
В этой строке Object
считается статическим типом переменной, а ClassA
считается динамическим типом переменной.
Статические типы всегда используются, чтобы гарантировать существование поля или метода компилятором, а динамические типы - это то, что фактически используется для вызова методов при запуске программы.
Идея такова: поскольку статический тип всегда должен быть суперклассом динамического типа, статический тип используется, чтобы гарантировать, что независимо от того, какие подклассы вы используете в качестве динамического типа переменной, они всегда будут по меньшей мере поддерживать методы суперкласса. Например, каждый объект имеет метод hashCode() и метод toString(), поскольку эти методы считаются существенными для всех объектов Java. Вам всегда будет позволено называть obj1.hashCode()
.
Однако, поскольку класс Object не содержит whoAmI()
, компилятор whoAmI()
ошибку.
Скажем, у вас было это
Object ref;
if (new Random().nextInt(2) % 2 == 0)
ref = new ClassA();
else
ref = new String("whatever");
Должен ли вы ссылаться на метод whoAmI
объекта, на который ссылается переменная ref
? Нет. Компилятор не может постоянно знать, какие типы объектов будут храниться во время выполнения. Таким образом, методы решаются во время компиляции в зависимости от типа переменных. Тип Object
не имеет метода whoAmI
.