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

doIt 기능은 "아빠"를 인쇄합니다. "아들"을 인쇄하는 방법이 있습니까?


111
보호 된 정적이 표시되면 실행하십시오.
Tom Hawtin-tackline

23
@ TomHawtin-tackline은 나의 무지를 용서하지만 왜 보호 된 정적이 눈살을 찌푸리게됩니까? 인터넷 검색을 시도했지만 명확한 답변을 찾을 수 없습니다. 감사합니다
Tony Chan

답변:


68

예. 그러나 변수와 관련하여 변수를 덮어 씁니다 (변수에 새로운 가치를 부여합니다. 함수에 새로운 정의를 부여하는 것은 재정의입니다).Just don't declare the variable but initialize (change) in the constructor or static block.

부모 클래스의 블록에서 사용할 때 값이 반영됩니다.

변수가 정적이면 정적 블록으로 초기화 자체에서 값을 변경하십시오.

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

또는 생성자에서 변경하십시오.

나중에 블록에서 값을 변경할 수도 있습니다. 그것은 슈퍼 클래스에 반영됩니다


10
가독성을 높이기 위해 답변에 코드가 있어야한다고 생각합니다. Son 구현을 다음과 같이 변경하십시오. class Son extends Dad { static { me = 'son' } }
Cléssio Mendes

97

간단히 말해서, 클래스 변수를 재정의하는 방법은 없습니다.

Java에서 클래스 변수를 재정의하지 마십시오. 재정의는 예를 들어 메소드입니다. 숨기는 것은 재정의와 다릅니다.

예를 들어, Son 클래스에서 이름이 'me'인 클래스 변수를 선언하여 클래스 이름이 'me'인 동일한 수퍼 클래스 Dad에서 상속 된 클래스 변수를 숨 깁니다. 이런 식으로 변수를 숨기는 것은 수퍼 클래스 Dad에서 클래스 변수 'me'의 값에 영향을 미치지 않습니다.

질문의 두 번째 부분, "son"을 인쇄하는 방법에 대해서는 생성자를 통해 값을 설정했습니다. 아래 코드는 원래 질문과는 많이 다르지만 다음과 같이 작성합니다.

public class Person {
    private String name;

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

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

JLS 섹션 8.3-필드 선언 에서 숨기기에 대한 자세한 정보를 제공합니다.


1
오버라이드 파생 된 클래스의 정적 블록을 가짐으로써 가능
siddagrl

1
@ siddagrl : 당신은 정교한 수 있습니까?
Mr_and_Mrs_D

Person이 예제에서 어떤 OP 클래스가 대체되어야합니까?
n611x007

1
@naxa SonDad에서 상속받은 Person다음 super("Son or Dad");생성자 를 호출 합니다.
Panzercrisis 2016 년

Java에서와 같은 변수를 숨기는 것이 좋거나 나쁘다고 간주됩니까?
Panzercrisis 2016 년

31

예, printMe()메소드를 재정의하십시오 .

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

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

사용중인 라이브러리에 printMe()메소드가 없거나 정적 메소드 인 경우 어떻게해야합니까?
Venkat Sudheer Reddy Aedama

1
보다 실제적인 예를 만들기 위해 "printMe"메소드는 반복하고 싶지 않은 논리를 가진 매우 복잡한 함수일 수 있습니다. 이 예에서는 사람을 인쇄하는 논리가 아니라 사람의 이름 만 변경하려고합니다. "son"을 리턴하기 위해 대체 할 "getName"이라는 메소드를 작성해야하며 printMe 메소드는 인쇄의 일부로 getName 메소드를 호출합니다.
Ring

17

getter를 작성한 다음 해당 getter를 대체 할 수 있습니다. 재정의하는 변수가 자체의 하위 클래스 인 경우 특히 유용합니다. 수퍼 클래스에 Object멤버 가 있다고 가정 하지만 하위 클래스에서이 클래스가 더 정의되어 있습니다 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"
}

11

재정의하려는 경우이 정적을 유지 해야하는 유효한 이유가 표시되지 않습니다. 추상화 사용을 제안합니다 (예제 코드 참조). :

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

이제 아빠를 추가 할 수 있습니다 :

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

아들:

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

그리고 아빠는 좋은 여자를 만났다 :

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

우리는 가족이있는 것처럼 보이고 세상에 그들의 이름을 알려줍니다.

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 출력 : Tommy Nancy Dad
System.err는 위와 동일합니다 (빨간색 글꼴이 있습니다)
JOption 출력 :
Tommy ,
Nancy ,
Dad


1
OP는 정적에 대한 액세스 변경과 관련이있었습니다. 따라서 "me"는 일반적인 "아빠"또는 "아들"이었습니다. 모든 아빠 나 아들을 위해 만들 필요가 없기 때문에 정적 인 것입니다.
RichieHH

그래, 네 권리, 나는 그것이 정적 인 것을 보지 못했다. 하하, 내 대답을 100 줄 이하의 코드 줄로 바꿀 것입니다. 고마워요
nckbrz

6

이것은 디자인 결함처럼 보입니다.

정적 키워드를 제거하고 예를 들어 생성자에서 변수를 설정하십시오. 이런 식으로 Son은 자신의 생성자에서 변수를 다른 값으로 설정합니다.


1
이것에 대해 무엇이 잘못 되었습니까? 디자인에서 'me'멤버 변수를 무시할 수있는 경우 패트릭이 올바른 솔루션입니다
Chii

실제로, 모든 인스턴스에 대해 저의 가치가 동일하다면 '정적'을 제거하는 것이 좋습니다. 초기화는 생성자에있을 필요는 없습니다.
Nate Parsons

오른쪽, 기술적 있지만 (바이트 코드에서) 나는 ;-) 거의 같은 생각
패트릭 코넬리

4

클래스 변수는 서브 클래스에만 숨겨지고 재정의되지는 않지만 printMe (), 서브 클래스 를 재정의하지 않고 원하는 것을 수행 할 수 있으며 리플렉션은 친구입니다. 아래 코드에서 명확성을 위해 예외 처리를 생략했습니다. 선언 있습니다 me으로하는 protected이 서브 클래스에 숨겨져있을 것입니다,이 맥락에서 많은 감각을 갖고있는 것 같다하지 않습니다 ...

class Dad
  {
    static String me = "dad";

    public void printMe ()
      {
        java.lang.reflect.Field field = this.getClass ().getDeclaredField ("me");
        System.out.println (field.get (null));
      }
  }

매우 흥미 롭습니다. 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();
}

... "아들"을 인쇄합니다.


이것은 원래 질문과 동일하지 않습니까?
Corley Brigman

이유를 설명해 주시겠습니까? (답변에)
Mr_and_Mrs_D

3

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

필드 숨기기라고합니다.

위의 링크에서

클래스 내에서 수퍼 클래스의 필드와 이름이 같은 필드는 유형이 다르더라도 수퍼 클래스의 필드를 숨 깁니다. 서브 클래스 내에서 수퍼 클래스의 필드는 단순 이름으로 참조 될 수 없습니다. 대신, 다음 섹션에서 다루는 super를 통해 필드에 액세스해야합니다. 일반적으로 코드를 읽기가 어렵 기 때문에 필드를 숨기는 것은 좋지 않습니다.


1
잠재적 인 솔루션에 대한 링크는 언제나 환영하지만 링크 주위에 컨텍스트추가 하여 동료 사용자가 그 이유와 그 이유를 알 수 있도록하십시오. 대상 사이트에 도달 할 수 없거나 영구적으로 오프라인 상태가되는 경우 항상 중요한 링크의 가장 관련성있는 부분을 인용하십시오. 외부 사이트에 대한 링크 이상으로 간신히 존재 하는 것은 왜 그리고 어떻게 일부 답변이 삭제되는지에 대한 가능한 이유라는 점을 고려하십시오 . .
FelixSFD

2

재정 의하여 만 printMe():

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

메소드 의 참조 meDad.printMe암시 적으로 static 필드를 가리 키 Dad.me므로 어떤 방식 으로든 변경하는 방식 printMe입니다 Son...


2

클래스의 변수를 재정의 할 수 없습니다. 메소드 만 대체 할 수 있습니다. 변수를 비공개로 유지해야합니다. 그렇지 않으면 많은 문제가 발생할 수 있습니다.


정적을 재정의 할 수 없습니다.
Tom Hawtin-tackline

(또는 적어도 이해가되지 않습니다, IFSWIM.)
Tom Hawtin-tackline

맞습니다. 정적을 재정의 할 수 없습니다. 당신은 그들 또는 일부 사람들이 나를 매스 킹이라고 할 수 있습니다.
데일

2

필드가 무시되지 않고 숨겨져 있기 때문에 실제로 '아빠'를 인쇄합니다. '아들'을 인쇄하는 방법에는 세 가지가 있습니다.

접근법 1 : 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();
}

접근 방식 2 : 필드를 숨기지 않고 생성자에서 초기화하지 마십시오.

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

접근법 3 : 정적 값을 사용하여 생성자에서 필드를 초기화하십시오.

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

2

변수는 오버린 딩에 참여하지 않습니다. 방법 만이 가능합니다. 메소드 호출은 런타임에 해결됩니다. 즉, 메소드 호출 결정은 런타임에 수행되지만 변수는 컴파일 시간에만 결정됩니다. 따라서 해당 런타임 변수가 아닌 호출에 참조가 사용되는 변수가 호출됩니다.

다음 스 니펫을 살펴보십시오.

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

}

코드를 실행하면 콘솔에 다음이 표시됩니다.

Inside Honda
Inside Honda
Inside bike

Max_Speed = 150
Max_Speed = 90
Max_Speed = 90

0

물론 개인 속성과 getter 및 setter를 사용하는 것이 권장되는 방법이지만 다음을 테스트하여 작동합니다 ... 코드의 주석을 참조하십시오

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 ... 난 그냥 상속 규칙을 재정의 했습니까 아니면 오라클을 까다로운 상황에 놓았습니까? 이 프로그램을 실행할 때 알 수 있듯이 보호 된 정적 문자열 me가 분명히 재정의됩니다. 또한 왜 속성을 재정의해서는 안되는지 이해가되지 않습니다.


1
이 경우 수퍼 클래스에서 변수를 "숨기고"있습니다. 이것은 재정의와 구별되며 상속과 관련이 없습니다. 다른 클래스는 기본 클래스 또는 하위 클래스를 참조했는지 여부에 따라 하나의 변수 또는 다른 변수를 볼 수 있으며 표시되는 클래스는 컴파일 타임에 고정됩니다. 서브 클래스에서 "me"라는 이름을 다른 것으로 바꾸면 같은 효과를 얻을 수 있습니다. 대부분의 IDE 및 코드 유효성 검사 도구 (핀 버그 등)는 일반적으로 수행하려는 작업이 아니기 때문에 이와 같은 변수를 숨길 때 경고합니다.
AutomatedMike

코딩 만의 관점에서 프로그래밍을보고 있습니다. 그러나이 경우 원하는 코드는 괜찮습니다. 팀 구성원의 관점에서 코딩을 살펴보면 필드를 재정의 할 수없는 이유와 같은 규칙이 명확 해집니다. 나는 왜 당신에 대한 연설을 할 수 있지만, 당신이 팀원의 관점에서 그것을 볼 수 없다면, 당신은 내 요점을 유효하지 않은 것으로보고 무효 한 주장을 다시 내게 던질 것입니다.
nckbrz

0

서브 클래스에서 변수를 쉽게 재 할당 할 수 있는데 왜 변수를 대체하려고합니까?

언어 디자인을 해결하기 위해이 패턴을 따릅니다. 여러 파생 응용 프로그램에서 서로 다른 특징으로 사용해야하는 프레임 워크에 중요한 서비스 클래스가있는 경우를 가정합니다.이 경우 수퍼 클래스 논리를 구성하는 가장 좋은 방법은 '정의'변수를 다시 할당하는 것입니다.

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

}

0

아니요. 클래스 변수 (인스턴스 변수에도 적용 가능)는 호출 변수의 유형에 따라 클래스 변수가 호출되므로 Java에서 재정의 기능을 나타내지 않습니다. 더 명확하게하기 위해 계층 구조에 하나 이상의 클래스 (인간)를 추가했습니다. 이제 우리는

아들은 아빠를 확장 인간을 확장

아래 코드에서는 Human, Dad 및 Son 객체의 배열을 반복하려고 시도하지만 호출 객체의 유형이 Human 인 경우 모든 경우에 Human Class의 값을 인쇄합니다.

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

인쇄합니다

  • 인간
  • 인간
  • 인간

따라서 클래스 변수를 재정의하지 않습니다.

부모 클래스의 참조 변수에서 실제 객체의 클래스 변수에 액세스하려면 부모 참조 (Human object)를 해당 유형으로 캐스팅하여 명시 적으로 컴파일러에게 알려야합니다.

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

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

인쇄합니다

  • 아빠
  • 아들

이 질문의 일부에 :-이미 제안했듯이 Son 클래스에서 printMe () 메서드를 재정의 한 다음 호출 할 때

Son().printMe();

아빠 클래스 변수 "me"는 Son 클래스에서 "me"의 가장 가까운 선언 (Son 클래스 printme () 메소드)이 우선 순위를 갖기 때문에 숨겨집니다.


0

하위 클래스 생성자에서 super.variable을 호출하십시오.

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

}

}

출력은 10입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.