Existe uma maneira de substituir variáveis ​​de classe em 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();
}

A função doIt irá imprimir "pai". Existe uma maneira de fazê-lo imprimir "filho"?

Dikla
fonte
111
Se você vir uma estática protegida, execute.
Tom Hawtin # tackline 26/03/09
23
@ TomHawtin-tackline perdoa minha ignorância, mas por que uma estática protegida é desaprovada? Tentei pesquisar no Google, mas não consigo encontrar uma resposta clara. Obrigado
Tony Chan

Respostas:

68

Sim. Mas, no que diz respeito à variável, ela é substituída (Dar novo valor à variável. Dar nova definição à função é Substituir).Just don't declare the variable but initialize (change) in the constructor or static block.

O valor será refletido ao usar nos blocos da classe pai

se a variável for estática, altere o valor durante a própria inicialização com o bloco estático,

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

ou então mudança no construtor.

Você também pode alterar o valor posteriormente em qualquer bloco. Será refletido na super classe

Vivek MVK
fonte
10
Eu acho que a resposta deve ter algum código para melhor legibilidade. Basta alterar a implementação do filho para class Son extends Dad { static { me = 'son' } }
Cléssio Mendes 22/15
97

Em resumo, não, não há como substituir uma variável de classe.

Você não substitui as variáveis ​​de classe em Java, mas as oculta. Substituir é, por exemplo, métodos. Ocultar é diferente de substituir.

No exemplo que você deu, declarando a variável de classe com o nome 'eu' na classe Filho, você oculta a variável de classe que ela herdaria de sua superclasse Pai, com o mesmo nome 'eu'. Ocultar uma variável dessa maneira não afeta o valor da variável de classe 'eu' na superclasse Pai.

Para a segunda parte da sua pergunta, sobre como fazê-lo imprimir "filho", eu definiria o valor através do construtor. Embora o código abaixo se afaste bastante da sua pergunta original, eu escreveria algo assim;

public class Person {
    private String name;

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

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

O JLS fornece muito mais detalhes sobre como ocultar na seção 8.3 - Declarações de campo

Mike
fonte
1
Substituição é possível por ter um bloco estático na classe derivada
siddagrl
1
@siddagrl: você pode elaborar?
Mr_and_Mrs_D
qual das classes do OP Persondeve substituir neste exemplo?
N611x007
1
@ naxa Sone Daddevem herdar de e Person, em seguida, chamar super("Son or Dad");seus construtores.
Panzercrisis
É considerado bom ou ruim ocultar variáveis ​​como essa em Java?
Panzercrisis
31

Sim, basta substituir o printMe()método:

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

        @Override
        public void printMe() {
                System.out.println(me);
        }
}
Romain Linsolas
fonte
e se a biblioteca que você está usando não tiver printMe()método, ou mesmo tiver esse método, é um método estático?
Venkat Sudheer Reddy Aedama
1
Para tornar esse exemplo mais real, você pode considerar que o método "printMe" pode ser uma função muito complicada com lógica que você não deseja repetir. Neste exemplo, você apenas deseja alterar o nome da pessoa, não a lógica que a imprime. Você deve criar um método chamado "getName" que substituiria para retornar "son" e o método printMe chamaria o método getName como parte de sua impressão.
Ring
17

Você pode criar um getter e, em seguida, substituí-lo. É particularmente útil se a variável que você está substituindo for uma subclasse própria. Imagine que sua super turma tem um Objectmembro, mas na sua subclasse isso agora está mais definido para ser um 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
fonte
11

Se você deseja substituí-lo, não vejo um motivo válido para manter isso estático. Eu sugeriria o uso de abstração (veja o código de exemplo). :

     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);
    }

Agora podemos adicionar o pai:

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;
    }
}

o filho:

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;
    }
}

e papai conheceu uma boa moça:

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 temos uma família, vamos dizer ao mundo seus nomes:

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());
    }
}

}

System.out Saída: Tommy Nancy Dad
System.err é o mesmo que acima (apenas com fonte vermelha)
JOption Output:
Tommy, depois
Nancy e depois
Papai

nckbrz
fonte
1
O OP estava relacionado à alteração do acesso à estática. Portanto, o "eu" era um "pai" ou "filho" genérico - algo que não precisa ser criado para todo pai ou filho e, portanto, é estático.
11133 RichieHH
Sim, você está certo, eu não vi que era estático. Haha, isso mudaria minha resposta para cerca de 100 linhas a menos de código, se respondesse. Obrigado pela
atenção
6

Isso parece uma falha de design.

Remova a palavra-chave estática e defina a variável, por exemplo, no construtor. Dessa maneira, Son apenas define a variável para um valor diferente em seu construtor.

Patrick Cornelissen
fonte
1
o que há de errado nisso? Se o 'eu' variável de membro é suposto ser substituível em seu projeto, em seguida, patrick é a solução correta
Chii
Na verdade, se o meu valor for o mesmo para todas as instâncias, basta remover 'estático'. A inicialização não precisa estar no construtor.
Nate Parsons
Certo, embora tecnicamente (no bytecode) Eu acho que é quase o mesmo ;-)
Patrick Cornelissen
4

Embora seja verdade que as variáveis ​​de classe só podem estar ocultas nas subclasses e não serem substituídas, ainda é possível fazer o que você deseja sem substituir as printMe ()subclasses, e a reflexão é sua amiga. No código abaixo, omito o tratamento de exceções para maior clareza. Observe que declarar mecomo protectednão parece ter muito sentido nesse contexto, pois ficará oculto nas subclasses ...

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
fonte
Muito interessante, Java.lang.reflect hein? ... +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á "filho".

user2895864
fonte
não é o mesmo que a pergunta original?
Corley Brigman
Você pode explicar o porquê? (na sua resposta)
Mr_and_Mrs_D 30/10
3

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

Chama-se Ocultando Campos

No link acima

Dentro de uma classe, um campo que tem o mesmo nome de um campo na superclasse oculta o campo da superclasse, mesmo que seus tipos sejam diferentes. Dentro da subclasse, o campo na superclasse não pode ser referenciado por seu nome simples. Em vez disso, o campo deve ser acessado através de super, que é abordado na próxima seção. De um modo geral, não recomendamos ocultar campos, pois isso dificulta a leitura do código.

sadjkas sadal
fonte
1
Um link para uma solução em potencial é sempre bem-vindo, mas adicione contexto ao link para que seus colegas usuários tenham uma idéia do que é e por que está lá. Sempre cite a parte mais relevante de um link importante, caso o site de destino esteja inacessível ou fique permanentemente offline. Leve em conta que ser pouco mais que um link para um site externo é uma possível razão para Por que e como algumas respostas são excluídas? .
precisa saber é o seguinte
2

apenas substituindo printMe():

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

a referência a meno Dad.printMemétodo aponta implicitamente para o campo estático Dad.me, de uma maneira ou de outra você está mudando o que printMefaz em Son...

Dan Vinton
fonte
2

Você não pode substituir variáveis ​​em uma classe. Você pode substituir apenas métodos. Você deve manter as variáveis ​​privadas, caso contrário, poderá obter muitos problemas.

Ionel Bratianu
fonte
Você não pode substituir nenhuma estática.
Tom Hawtin - tackline
(ou pelo menos não faz sentido, IFSWIM.)
Tom Hawtin - tackline
Correto, você não pode substituir as estáticas. Você pode sombra-los ou algumas pessoas me chamaram de máscara.
Dale
2

Na verdade, imprime 'pai', pois o campo não é substituído, mas oculto. Existem três abordagens para imprimir 'filho':

Abordagem 1: substituir 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();
}

Abordagem 2: não oculte o campo e inicialize-o no construtor

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();
}

Abordagem 3: use o valor estático para inicializar um campo no construtor

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
fonte
2

Variáveis ​​não participam da substituição. Somente métodos fazem. Uma chamada de método é resolvida no tempo de execução, ou seja, a decisão de chamar um método é tomada no tempo de execução, mas as variáveis ​​são decididas apenas no tempo de compilação. Portanto, essa variável é chamada cuja referência é usada para chamar e não do objeto de tempo de execução.

Dê uma olhada no seguinte trecho:

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);
  }

}

Quando você executa o código, o console mostra:

Inside Honda
Inside Honda
Inside bike

Max_Speed = 150
Max_Speed = 90
Max_Speed = 90
Ananta SE
fonte
0

É claro que usar atributos privados, e getters e setters, seria a coisa recomendada a se fazer, mas eu testei o seguinte e funciona ... Veja o comentário no 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();
    }
}

Sooo ... acabei de redefinir as regras de herança ou coloquei o Oracle em uma situação complicada? Para mim, a String estática protegida me é claramente substituída, como você pode ver quando executa este programa. Além disso, não faz sentido para mim porque os atributos não devem ser substituíveis.

Pixel
fonte
1
Nesse caso, você está "ocultando" a variável da superclasse. Isso é diferente de substituir e não tem nada a ver com herança. Outras classes verão uma variável ou a outra, dependendo se elas referenciaram a classe base ou a subclasse e a que elas vêem é fixada no tempo de compilação. Se você alterasse o nome "eu" na subclasse para outra coisa, obteria o mesmo efeito. A maioria dos IDEs e ferramentas de validação de código (findbugs etc.) avisam quando você oculta variáveis ​​como essa, pois geralmente não é o que você deseja fazer.
AutomatedMike
Você está olhando para a programação apenas da perspectiva da codificação. O que é bom, codifique como quiser nesse caso. Se você observar a codificação da exibição de um membro da equipe, as regras ficarão claras, como o motivo pelo qual os campos não são substituíveis e assim por diante. Eu poderia fazer um discurso sobre o porquê, mas se você não o vê dessa perspectiva de um membro da equipe, verá meus pontos como inválidos e lançará argumentos inválidos para mim.
Nckbrz 14/04/2014
0

Por que você deseja substituir variáveis ​​quando é possível reatribuí-las facilmente nas subclasses.

Sigo esse padrão para solucionar o design da linguagem. Suponha um caso em que você tenha uma classe de serviço pesada em sua estrutura que precise ser usada em diferentes tipos de aplicações derivadas. Nesse caso, a melhor maneira de configurar a lógica da superclasse é reatribuir suas variáveis ​​'definidoras'.

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
fonte
0

Não. As variáveis ​​de classe (também aplicáveis ​​às variáveis ​​de instância) não exibem o recurso de substituição em Java, pois as variáveis ​​de classe são chamadas com base no tipo de objeto de chamada. Adicionada mais uma classe (Humana) na hierarquia para torná-la mais clara. Então agora nós temos

Filho estende Papai estende Humano

No código abaixo, tentamos iterar sobre uma matriz de objetos Human, Dad e Son, mas ele imprime os valores da Human Class em todos os casos, como o tipo de objeto de chamada como Human.

    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
        }
    }
}

Vai imprimir

  • humano
  • humano
  • humano

Portanto, nenhuma substituição de variáveis ​​de classe.

Se queremos acessar a variável de classe do objeto real a partir de uma variável de referência de sua classe pai, precisamos explicitamente informar isso ao compilador lançando a referência pai (objeto humano) para seu tipo.

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

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

Vai imprimir

  • Papai
  • filho

Sobre como parte desta pergunta: - Como já sugerido, substitua o método printMe () na classe Son e, em seguida, chamando

Son().printMe();

A variável de classe do pai "me" ficará oculta porque a declaração mais próxima (do método printme () da classe Son) do "me" (na classe Son) terá precedência.

Desenvolvedor
fonte
0

Basta chamar super.variable no construtor de subclasses

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());

}

}

A saída é 10.

fractal80y
fonte