¿Hay alguna forma de anular las variables de clase en Java?

161
class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String me = "son";
}

public void doIt()
{
    new Son().printMe();
}

La función imprimirá "papá". ¿Hay alguna manera de hacer que imprima "hijo"?

Dikla
fuente
111
Si alguna vez ve una estática protegida, ejecute.
Tom Hawtin - tackline
23
@ TomHawtin-tackline perdona mi ignorancia, pero ¿por qué una estática protegida está mal vista? Intenté buscar en Google pero no puedo encontrar una respuesta clara. Gracias
Tony Chan
16
Verifique esta pregunta: ¿Por qué no debemos usar estática protegida en Java?
Zeeshan

Respuestas:

68

Si. Pero en lo que respecta a la variable, se sobrescribe (dar un nuevo valor a la variable. Dar una nueva definición a la función es anular).Just don't declare the variable but initialize (change) in the constructor or static block.

El valor se reflejará cuando se use en los bloques de la clase padre

si la variable es estática, cambie el valor durante la inicialización con el bloque estático,

class Son extends Dad {
    static { 
       me = 'son'; 
    }
}

o bien cambio en el constructor.

También puede cambiar el valor más adelante en cualquier bloque. Se reflejará en super clase

Vivek MVK
fuente
10
Creo que la respuesta debería tener algún código para una mejor legibilidad. Simplemente cambie la implementación de Son a class Son extends Dad { static { me = 'son' } }
Cléssio Mendes
97

En resumen, no, no hay forma de anular una variable de clase.

No anula las variables de clase en Java, las oculta. La anulación es, por ejemplo, los métodos. Ocultar es diferente de anular.

En el ejemplo que ha dado, al declarar la variable de clase con el nombre 'yo' en la clase Hijo, oculta la variable de clase que habría heredado de su superclase Dad con el mismo nombre 'yo'. Ocultar una variable de esta manera no afecta el valor de la variable de clase 'yo' en la superclase Dad.

Para la segunda parte de su pregunta, sobre cómo hacer que se imprima "hijo", establecería el valor a través del constructor. Aunque el código a continuación se aparta bastante de su pregunta original, yo escribiría algo como esto;

public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public void printName() {
        System.out.println(name);
    }
}

El JLS da muchos más detalles sobre cómo esconderse en la sección 8.3 - Declaraciones de campo

Miguel
fuente
1
La anulación es posible al tener un bloque estático en la clase derivada
siddagrl
1
@siddagrl: ¿puedes dar más detalles?
Mr_and_Mrs_D
¿Cuál de las clases de OP se Personsupone que debe reemplazar en este ejemplo?
n611x007
1
@naxa Sony Dadse supone que heredan de Person, luego llaman super("Son or Dad");a sus constructores.
Panzercrisis
¿Se considera bueno o malo ocultar variables como esa en Java?
Panzercrisis
31

Sí, simplemente anule el printMe()método:

class Son extends Dad {
        public static final String me = "son";

        @Override
        public void printMe() {
                System.out.println(me);
        }
}
Romain Linsolas
fuente
¿Qué pasa si la biblioteca que está utilizando no tiene printMe()método, o incluso si tiene ese método, es un método estático?
Venkat Sudheer Reddy Aedama
1
Para hacer de este un ejemplo de la vida real, podría considerar que el método "printMe" podría ser una función muy complicada con una lógica que no desea repetir. En este ejemplo, solo desea cambiar el nombre de la persona, no la lógica que lo imprime. Debe crear un método llamado "getName" que anularía para devolver "son" y el método printMe invocaría el método getName como parte de su impresión.
Anillo
17

Puede crear un captador y luego anularlo. Es particularmente útil si la variable que está anulando es una subclase de sí misma. Imagine que su superclase tiene un Objectmiembro pero en su subclase ahora está más definido para ser un Integer.

class Dad
{
        private static final String me = "dad";

        protected String getMe() {
            return me;
        }

        public void printMe()
        {
                System.out.println(getMe());
        }
}

class Son extends Dad
{
        private static final String me = "son";

        @Override
        protected String getMe() {
            return me;
        }
}

public void doIt()
{
        new Son().printMe(); //Prints "son"
}
John Strickler
fuente
11

Si lo va a anular, no veo una razón válida para mantener esto estático. Sugeriría el uso de la abstracción (ver código de ejemplo). :

     public interface Person {
        public abstract String getName();
       //this will be different for each person, so no need to make it concrete
        public abstract void setName(String name);
    }

Ahora podemos agregar al papá:

public class Dad implements Person {

    private String name;

    public Dad(String name) {
        setName(name);
    }

    @Override
    public final String getName() {
    return name;
    }

    @Override
    public final void setName(String name) {
        this.name = name;
    }
}

el hijo:

public class Son implements Person {

    private String name;

    public Son(String name) {
        setName(name);
    }

    @Override
    public final String getName() {
        return name;
    }

    @Override
    public final void setName(String name) {
        this.name = name;
    }
}

y papá conoció a una buena dama:

public class StepMom implements Person {

    private String name;

    public StepMom(String name) {
        setName(name);
    }

    @Override
    public final String getName() {
        return name;
    }

    @Override
    public final void setName(String name) {
        this.name = name;
    }
}

Parece que tenemos una familia, digamos al mundo sus nombres:

public class ConsoleGUI {

    public static void main(String[] args) {
        List<Person> family = new ArrayList<Person>();
        family.add(new Son("Tommy"));
        family.add(new StepMom("Nancy"));
        family.add(new Dad("Dad"));
        for (Person person : family) {
            //using the getName vs printName lets the caller, in this case the
            //ConsoleGUI determine versus being forced to output through the console. 
            System.out.print(person.getName() + " ");
            System.err.print(person.getName() + " ");
            JOptionPane.showMessageDialog(null, person.getName());
    }
}

}

Salida System.out: Tommy Nancy Dad
System.err es el mismo que el anterior (solo tiene una fuente roja)
Salida JOption:
Tommy luego
Nancy y luego
Dad

nckbrz
fuente
1
El OP se refería a cambiar el acceso a las estadísticas. Por lo tanto, el "yo" era un "padre" o "hijo" genérico, algo que no necesita crearse para cada padre o hijo y, por lo tanto, es estático.
RichieHH
Sí, tienes razón, no vi que fuera estático. Jaja, eso cambiaría mi respuesta a unas 100 líneas de código menos, si respondiera. Gracias por el
aviso
6

Esto parece un defecto de diseño.

Elimine la palabra clave estática y establezca la variable, por ejemplo, en el constructor. De esta manera, Son simplemente establece la variable en un valor diferente en su constructor.

Patrick Cornelissen
fuente
1
¿Qué tiene de incorrecto esto? Si se supone que la variable miembro 'yo' debe ser reemplazable en su diseño, entonces patrick es la solución correcta
Chii
En realidad, si el valor de mí es el mismo para cada instancia, simplemente eliminar 'estático' estaría bien. La inicialización no tiene que estar en constructor.
Nate Parsons el
Correcto, aunque técnicamente (en bytecode) creo que es casi lo mismo ;-)
Patrick Cornelissen el
4

Aunque es cierto que las variables de clase solo pueden ocultarse en subclases y no anularse, aún es posible hacer lo que quiera sin anular printMe ()en subclases, y la reflexión es su amiga. En el siguiente código, omito el manejo de excepciones para mayor claridad. Tenga en cuenta que declarar mecomo protectedno parece tener mucho sentido en este contexto, ya que estará oculto en las subclases ...

class Dad
  {
    static String me = "dad";

    public void printMe ()
      {
        java.lang.reflect.Field field = this.getClass ().getDeclaredField ("me");
        System.out.println (field.get (null));
      }
  }
Sergey Ushakov
fuente
Muy interesante, Java.lang.reflect huh? ... +1
nckbrz
3
class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String _me = me = "son";
}

public void doIt()
{
    new Son().printMe();
}

... imprimirá "hijo".

usuario2895864
fuente
¿No es esto lo mismo que la pregunta original?
Corley Brigman
Puedes explicar porque ? (en su respuesta)
Mr_and_Mrs_D
3

https://docs.oracle.com/javase/tutorial/java/IandI/hidevariables.html

Se llama Ocultar campos

Desde el enlace de arriba

Dentro de una clase, un campo que tiene el mismo nombre que un campo en la superclase oculta el campo de la superclase, incluso si sus tipos son diferentes. Dentro de la subclase, el campo en la superclase no puede ser referenciado por su nombre simple. En su lugar, se debe acceder al campo a través de super, que se trata en la siguiente sección. En términos generales, no recomendamos ocultar campos, ya que dificulta la lectura del código.

sadjkas sadal
fuente
1
Siempre es bienvenido un enlace a una solución potencial, pero agregue contexto alrededor del enlace para que sus usuarios tengan una idea de qué es y por qué está allí. Siempre cite la parte más relevante de un enlace importante, en caso de que no se pueda acceder al sitio de destino o se desconecte permanentemente. Tenga en cuenta que ser apenas más que un enlace a un sitio externo es una posible razón de por qué y cómo se eliminan algunas respuestas. .
FelixSFD
2

solo anulando printMe():

class Son extends Dad 
{
    public void printMe() 
    {
        System.out.println("son");
    }
}

la referencia a meen el Dad.printMemétodo apunta implícitamente al campo estático Dad.me, por lo que de una forma u otra está cambiando lo que printMehace en Son...

Dan Vinton
fuente
2

No puede anular variables en una clase. Puede anular solo los métodos. Debe mantener las variables privadas, de lo contrario puede tener muchos problemas.

Ionel Bratianu
fuente
No puede anular ninguna estadística.
Tom Hawtin - tackline
(o al menos no tiene sentido, IFSWIM.)
Tom Hawtin - tackline
Correcto, no puedes anular las estadísticas. Puedes SOMBRARLAS o algunas personas me llamaron MÁSCARA.
Dale
2

De hecho, imprime 'papá', ya que el campo no se anula sino que se oculta. Hay tres enfoques para hacerlo imprimir 'hijo':

Enfoque 1: anular printMe

class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String me = "son";

    @override
    public void printMe()
    {
        System.out.println(me);
    }
}

public void doIt()
{
    new Son().printMe();
}

Enfoque 2: no oculte el campo e inicialícelo en el constructor

class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    public Son()
    {
        me = "son";
    }
}

public void doIt()
{
    new Son().printMe();
}

Enfoque 3: use el valor estático para inicializar un campo en el constructor

class Dad
{
    private static String meInit = "Dad";

    protected String me;

    public Dad() 
    {
       me = meInit;
    }

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    private static String meInit = "son";

    public Son()
    {
        me = meInit;
    }

}

public void doIt()
{
    new Son().printMe();
}
Rik Schaaf
fuente
2

Las variables no participan en la sobreescritura. Solo los métodos lo hacen. Una llamada a un método se resuelve en tiempo de ejecución, es decir, la decisión de llamar a un método se toma en tiempo de ejecución, pero las variables se deciden solo en tiempo de compilación. Por lo tanto, se llama a esa variable cuya referencia se usa para llamar y no al objeto de tiempo de ejecución.

Eche un vistazo al siguiente fragmento:

package com.demo;

class Bike {
  int max_speed = 90;
  public void disp_speed() {
    System.out.println("Inside bike");
 }
}

public class Honda_bikes extends Bike {
  int max_speed = 150;
  public void disp_speed() {
    System.out.println("Inside Honda");
}

public static void main(String[] args) {
    Honda_bikes obj1 = new Honda_bikes();
    Bike obj2 = new Honda_bikes();
    Bike obj3 = new Bike();

    obj1.disp_speed();
    obj2.disp_speed();
    obj3.disp_speed();

    System.out.println("Max_Speed = " + obj1.max_speed);
    System.out.println("Max_Speed = " + obj2.max_speed);
    System.out.println("Max_Speed = " + obj3.max_speed);
  }

}

Cuando ejecute el código, la consola mostrará:

Inside Honda
Inside Honda
Inside bike

Max_Speed = 150
Max_Speed = 90
Max_Speed = 90
Ananta SE
fuente
0

Por supuesto, usar atributos privados y getters y setters sería lo recomendado, pero probé lo siguiente y funciona ... Ver el comentario en el código

class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String me = "son";

    /* 
    Adding Method printMe() to this class, outputs son 
    even though Attribute me from class Dad can apparently not be overridden
    */

    public void printMe()
    {
        System.out.println(me);
    }
}

class Tester
{
    public static void main(String[] arg)
    {
        new Son().printMe();
    }
}

Entonces ... ¿acabo de redefinir las reglas de herencia o puse a Oracle en una situación difícil? Para mí, la cadena estática protegida me está claramente anulada, como puede ver cuando ejecuta este programa. Además, no tiene ningún sentido para mí por qué los atributos no deberían ser reemplazables.

Píxel
fuente
1
En este caso, está "ocultando" la variable de la superclase. Esto es distinto de anular y no tiene nada que ver con la herencia. Otras clases verán una variable u otra dependiendo de si hicieron referencia a la clase base o la subclase y la que ven está fija en tiempo de compilación. Si cambiara el nombre "yo" en la subclase a otra cosa, obtendría el mismo efecto. La mayoría de los IDE y las herramientas de validación de código (findbugs, etc.) le avisarán cuando oculte variables como esta, ya que generalmente no es lo que desea hacer.
AutomatedMike
Estás viendo la programación desde la perspectiva de la codificación sola. Lo cual está bien, codifique como quiera en ese caso. Si observa la codificación desde la vista de un miembro del equipo, entonces las reglas se vuelven claras, como por qué los campos no se pueden anular, etc. Podría darle un discurso sobre por qué, pero si no lo ve desde la perspectiva de un miembro del equipo, verá mis puntos como inválidos y me devolverá argumentos inválidos.
nckbrz
0

¿Por qué desearía anular variables cuando podría reasignarlas fácilmente en las subclases?

Sigo este patrón para evitar el diseño del lenguaje. Suponga un caso en el que tiene una clase de servicio importante en su marco que debe usarse en diferentes tipos en múltiples aplicaciones derivadas. En ese caso, la mejor manera de configurar la lógica de superclase es reasignando sus variables 'definitorias'.

public interface ExtensibleService{
void init();
}

public class WeightyLogicService implements ExtensibleService{
    private String directoryPath="c:\hello";

    public void doLogic(){
         //never forget to call init() before invocation or build safeguards
         init();
       //some logic goes here
   }

   public void init(){}    

}

public class WeightyLogicService_myAdaptation extends WeightyLogicService {
   @Override
   public void init(){
    directoryPath="c:\my_hello";
   }

}
premganz
fuente
0

No. Las variables de clase (también aplicables a las variables de instancia) no exhiben una función de anulación en Java ya que las variables de clase se invocan en función del tipo de objeto de llamada. Se agregó una clase más (Humano) en la jerarquía para que quede más claro. Entonces ahora tenemos

Hijo extiende papá extiende humano

En el siguiente código, intentamos iterar sobre una matriz de objetos Humanos, Papá e Hijo, pero imprime los valores de la Clase Humana en todos los casos, ya que el tipo de objeto de llamada era Humano.

    class Human
{
    static String me = "human";

    public void printMe()
    {
        System.out.println(me);
    }
}
class Dad extends Human
{
    static String me = "dad";

}

class Son extends Dad
{
    static String me = "son";
}


public class ClassVariables {
    public static void main(String[] abc)   {
        Human[] humans = new Human[3];
        humans[0] = new Human();
        humans[1] = new Dad();
        humans[2] = new Son();
        for(Human human: humans)   {
            System.out.println(human.me);        // prints human for all objects
        }
    }
}

Imprimirá

  • humano
  • humano
  • humano

Por lo tanto, no se anulan las variables de clase.

Si deseamos acceder a la variable de clase del objeto real desde una variable de referencia de su clase primaria, debemos decirle explícitamente esto al compilador mediante la conversión de la referencia primaria (objeto humano) a su tipo.

    System.out.println(((Dad)humans[1]).me);        // prints dad

    System.out.println(((Son)humans[2]).me);        // prints son

Imprimirá

  • papá
  • hijo

Sobre cómo parte de esta pregunta: - Como ya se sugirió, anule el método printMe () en la clase Son, luego al llamar

Son().printMe();

La variable "yo" de la clase de papá se ocultará porque la declaración más cercana (del método printme () de la clase Son) del "yo" (en la clase Son) tendrá prioridad.

Desarrollador
fuente
0

Simplemente llame a super.variable en el constructor de subclase

public abstract class Beverage {

int cost;


int getCost() {

    return cost;

}

}`

public class Coffee extends Beverage {


int cost = 10;
Coffee(){
    super.cost = cost;
}


}`

public class Driver {

public static void main(String[] args) {

    Beverage coffee = new Coffee();

    System.out.println(coffee.getCost());

}

}

La salida es 10.

fractal80y
fuente