Eu tenho uma pergunta sobre herança em linguagens de programação OO do tipo Java. Ele surgiu na minha classe de compilador, quando expliquei como compilar métodos e sua invocação. Eu estava usando Java como exemplo de linguagem de origem para compilar.
Agora considere este programa Java.
class A {
public int x = 0;
void f () { System.out.println ( "A:f" ); } }
class B extends A {
public int x = 1;
void f () { System.out.println ( "B:f" ); } }
public class Main {
public static void main ( String [] args ) {
A a = new A ();
B b = new B ();
A ab = new B ();
a.f();
b.f();
ab.f();
System.out.println ( a.x );
System.out.println ( b.x );
System.out.println ( ab.x ); } }
Quando você o executa, obtém o seguinte resultado.
A:f
B:f
B:f
0
1
0
Os casos interessantes são aqueles que acontecem com o objeto ab
do tipo estático A
, que é B
dinamicamente. Como ab.f()
imprime
B:f
segue que as invocações de método não são afetadas pelo tipo de tempo de compilação do objeto com o qual o método é chamado. Mas como é
System.out.println ( ab.x )
impressa 0
, o acesso dos membros é afetado pelos tipos de tempo de compilação.
Um aluno perguntou sobre essa diferença: o acesso de membros e métodos não deve ser consistente entre si? Não consegui encontrar uma resposta melhor do que "essa é a semântica do Java".
Você conheceria uma clara razão conceitual pela qual membros e métodos são diferentes nesse sentido? Algo que eu poderia dar aos meus alunos?
Edit : Após uma investigação mais aprofundada, isso parece ser uma idiossincrasia Java: C ++ e C # agem de maneira diferente, veja, por exemplo, o comentário de Saeed Amiri abaixo.
Edit 2 : Acabei de experimentar o programa Scala correspondente:
class A {
val x = 0;
def f () : Unit = { System.out.println ( "A:f" ); } }
class B extends A {
override val x = 1;
override def f () : Unit = { System.out.println ( "B:f" ); } }
object Main {
def main ( args : Array [ String ] ) = {
var a : A = new A ();
var b : B = new B ();
var ab : A = new B ();
a.f();
b.f();
ab.f();
System.out.println ( "a.x = " + a.x );
System.out.println ( "b.x = " + b.x );
System.out.println ( "ab.x = " + ab.x ); } }
E para minha surpresa, isso resulta em
A:f
B:f
B:f
a.x = 0
b.x = 1
ab.x = 1
Observe que os overrise
modificadores são necessários. Isso me surpreende porque o Scala compila na JVM e, além disso, quando eu compilo e executo o programa Java na parte superior usando o compilador / tempo de execução Scala, ele se comporta como o programa Java.
f
ex
, sem mencionar isso explicitamente, a versão C # do IMO é melhor ( mais aceitável do ponto vista OO), em vez de fazer isso, definax
,f
como virtual, e você obterá resultado consistência, substituindo-os emB
. Mas se você perguntar isso no Stackoverflow, receberá mais atenção e tenho certeza de uma ótima resposta.