2011-07-27 16 views
44

¿Por qué Java especifica que el especificador de acceso para un método de anulación puede permitir más, pero no menos, acceso que el método anulado? Por ejemplo, un método de instancia protegida en la superclase puede hacerse público, pero no privado, en la subclase.modificadores de acceso java y métodos de anulación

+6

Para usar la descripción común: 'B extiende A' si' B is-a A'. Entonces, si 'A' puede hacer' action() ', y' B is-a A', entonces 'B' debería ser capaz de hacer' action() 'también. – davin

+0

gracias a todos. Ahora entiendo que la declaración de extensión (A extiende B) también crea un esquema de herencia de acceso unidireccional (más abierto). – yesilupper

Respuesta

47

Es un principio fundamental de la programación orientada a objetos: la clase de niño es un ejemplo de pleno derecho de la clase padre, y debe, por tanto, presente al menos la misma interfaz como clase principal. Hacer las cosas protegidas/públicas menos visibles violaría esta idea; podría hacer que las clases secundarias no se puedan usar como instancias de la clase principal.

+1

De acuerdo con Patrick. Esto también es según el principio de sustitución Liskov – tapasvi

7

Porque sería raro:

class A { 
    public void blah() {} 
} 

class B extends A { 
    private void blah() {} 
} 


B b = new B(); 
A a = b; 
b.blah(); // Can't do it! 
a.blah(); // Can do it, even though it's the same object! 
+0

Creo que antes de cambiar, era un ejemplo más concreto, aunque esto es un poco más escandaloso, ya que podría definir un caso así para lanzar una excepción en tiempo de ejecución al acceder a un método extendido de manera privada (obviamente un término inventado). Pero en su ejemplo anterior uno debería funcionar, y el otro debe fallar. Tal vez incluirlos a los dos? – davin

+0

@davin: ¡No lo he cambiado! Acabo de mejorar el comentario del código ... –

+0

Oh, entonces imaginé que tenías 'A a = new A(); B b = new B(); 'o' A b = new B(); ' – davin

26

Imagínese estas dos clases:

public class Animal { 
    public String getName() { return this.name; } 
} 

public class Lion extends Animal { 
    private String getName() { return this.name; } 
} 

podría escribir este código:

Animal lion = new Lion(); 
System.out.println(lion.getName()); 

Y tendría que ser válido, ya que en Animal el método getName() es público, incluso si se hizo privado en Lion. Por lo tanto, no es posible hacer que las cosas sean menos visibles en las subclases, ya que una vez que tenga una referencia de superclase, podrá acceder a esta información.

+0

Te falta un 'extends Animal' :) – Jeffrey

+0

parece una mejor respuesta. – instinct

+0

De esto se trata el principio de sustitución de Liskov. – RainDoctor

0

Porque una subclase es una especialización de la superclase, o en otras palabras, es una extensión de la superclase.

Imagine por ejemplo el método toString. Todos los objetos Java lo tienen porque la clase Object lo tiene. Imagine que puede definir una clase con el método toString private. Entonces ya no tratarás a todos los objetos por igual. Por ejemplo, ya no debería ser capaz de esto con seguridad:

for (Object obj : collection) System.out.println(obj);

0

Bueno, en términos del caso específico que mencionaste, ¿cómo manejaría eso exactamente Java? Si la subclase hizo privado un método público/protegido, ¿qué debería hacer la JVM cuando se invoca ese método en una instancia de la subclase? ¿Honrar lo privado e invocar la implementación de la superclase? Además, está incumpliendo el contrato especificado por la superclase cuando de repente dice "nadie puede acceder a este método, a pesar de lo que inicialmente decía el contrato".

5

tomar un ejemplo dado a continuación

class Person{ 
public void display(){ 
     //some operation 
    } 
} 

class Employee extends Person{ 
    private void display(){ 
     //some operation 
    } 
} 

primordial Típica sucede en el caso siguiente

Person p=new Employee(); 

Aquí p es la referencia de objeto con la Persona tipo (superclase) cuando estamos llamando p .display(). A medida que el modificador de acceso es más restrictiva, la referencia objeto p no puede acceder a objeto secundario de tipo Empleado

1

tarde a la fiesta, pero me gustaría añadir una más preocupación relacionada con anulando: El método primordial debe permitir menos (o el mismo nivel de) excepción arrojable que el método reemplazado; incluso nada arrojable en absoluto.

principio de sustitución Liskov puede explicar eso también:

interface Actionable { 
    void action() throws DislocationException; 
} 

public class Actor implements Actionable { 
    @Override 
    public void action() throws DislocationException { 
    //.... 
    } 
} 

public class Stuntman implements Actionable { 
    @Override // this will cause compiler error 
    public void action() throws DislocationException, DeathException { 
    //.... 
    } 
} 

// legacy code to use Actionable 
try { 
    Actionable actor = new Actor(); // this cannot be replaced by a Stuntman, 
            // or it may break the try/catch block 
    actor.action(); 
} catch (DislocationException exc) { 
    // Do something else 
} 

Arriba, el método reemplazado se comprometió a que en el peor de los casos va a lanzar el DislocationException, no más (se requiere un médico en el lugar de rodaje) . Por lo que el método de alteración temporal no debe romper esa, añadiendo más DeathException (o una ambulancia es una necesidad)

menudo que yo llamo la regla primordial "[puede ser] un mayor acceso [nivel], [pero] menos excepción"

Cuestiones relacionadas