2009-09-21 10 views
3

class A 
{ 
    int i=10; 
    void show() 
    { 
    System.out.println("class A"); 
    } 
} 

class B extends A 
{ 
    int i=5; 
    public void show() 
    { 
    System.out.println("class B"); 
    } 
} 
class M 
{ 
    public static void main(String s[]) 
    { 
    A a=new B(); 
    a.show(); 
    System.out.println(a.i); 
    } 
} 


OUTPUT= class B 
     10 

클래스 A 메서드가 클래스 B 메서드로 재정의 된 경우 왜 변수 'i'가 필요하지 않습니까?메서드 재정의

+0

클래스 선언에 대한 내 노트를 확인하십시오. –

답변

5

변수가 가상이 아니므로 메서드 만 있습니다.

+0

"virtual"은 C++ 용어로 Java에서는 메소드 (또는 변수)를 "virtual"이라고 부르지 않습니다. – Jesper

+1

그의 문장은 여전히 ​​동일한 의미를 "덮어 씌워"라고 말하면 똑같은 의미입니다. –

+1

@matt b, 나는 "오버라이드 가능"을 의미한다고 생각하지만 그런 단어가 있는지 확실하지 않다. – vava

-3

기본적으로 변수는 private이기 때문에. 당신은 그것을 "보호 된"것으로 선언해야하고, 그런 다음 제대로 상속 될 것입니다.

+3

자바 클래스의 변수와 메소드의 기본 표시 여부는 비공개가 아닌 동일한 패키지의 다른 모든 클래스에서 볼 수있다. –

5

덮어 쓰지 않지만 숨김. 귀하의 결과물에서 구체적으로 a.i가 아닌 ((B) a) .i의 가치를 요청했습니다.

+1

왜 Java에서 필드가 대체되지 않습니까? Java에서 인스턴스 필드 조회는 컴파일시에 발생하기 때문에 Java는 컴파일시 계산 된 객체 오프셋 (offset)에있는 필드의 값을 객체의 메모리에 제공합니다 (이 경우 컴파일시 형식 정보를 기반으로합니다.이 경우'a' 유형 'A'로 선언 됨). –

+0

@Pavel, 대답 해 주시면 투표하겠습니다 :) – vava

+0

Ook, @vava. 이 의견을 좀 더 확장 된 답변의 기초로 사용했습니다. –

-1

a은 A의 인스턴스입니다. 생성자 B()를 호출합니다. 하지만 여전히 A 클래스입니다. 그 이유는 i가 10과 같습니다. 메서드의 재정의가 완료됩니다.

주 클래스는

public class A() 

만에하지 시작;

public class A { ... } 
4

이것은 구현의 "기능"입니다. 메모리,이 때문에 다음과 같습니다 당신이 B의 인스턴스에 i에 액세스 할 때

a: 
    pointer to class A 
    int i 

b: 
    pointer to class B 
    int i (from A) 
    int i (from B) 

는, 자바는 의미있는 변수를 알 필요가있다. 그것은 할당해야 모두 B에서 방법을 원하는 반면 클래스 A의 방법은 자신의 필드 i에 액세스 할 것이기 때문에 자신의 i (당신이 B에 새 필드 i을 만드는 대신 BA.i 볼 만들기로 결정했습니다부터). 즉, 두 개의 i이 있고 표준 공개 규칙이 적용됩니다. 둘 중 어느 쪽이 더 가깝습니까?

이제 A a=new B();이라고 말하면 Java는 "오른쪽에서 결과를 A 인 것처럼 취급합니다"라고 알려주기 때문에 약간 까다 롭습니다.

메서드를 호출하면 Java가 클래스 포인터 (메모리의 개체에서 첫 번째 것)를 따릅니다. 거기에서 메소드 목록을 찾습니다. 메소드가 서로 겹쳐 쓰므로 메소드 show()을 찾을 때 B에 정의 된 메소드를 찾습니다. 메소드 접근을 빠르게 만든다. 클래스 B의 (내부) 메소드 목록에있는 모든 보이는 메소드를 간단하게 합칠 수 있으며 각 호출은 그리스트에 대한 단일 액세스를 의미한다. 모든 클래스를 검색 할 필요가 없습니다.

필드 액세스가 비슷합니다. Java는 검색을 좋아하지 않습니다. 따라서 B b = new B();이라고 말하면 B에서 b.i이 분명합니다. 그러나 당신은 A a = new B()에게 자바에게 새로운 인스턴스를 A 타입으로 취급하는 것을 선호한다고 말했습니다. Java는 게으르다. A을 조사하고, 필드가 i 인 것을 확인하고, 그 필드를 볼 수 있고 더 이상 a의 실제 유형을 보지 않아도된다. (느릴 것이기 때문에) 느리고 b) 캐스팅하여 두 필드 모두에 액세스하지 못하도록합니다.

결국 Java는 필드 및 메소드 조회를 최적화하기 때문입니다.

+0

B b = new B()를 작성하고 B에서 재정의되지 않은 A의 메소드를 호출하면 호출이 B 유형에서 오는 것처럼 해당 메소드 내에서 10이되는 것입니까? – vava

+0

아니요, 그렇지 않습니다. 메소드는 구현 된 클래스에서 값을 가져옵니다. – vava

+1

'A'에 정의 된 메소드의 경우'i'의 값은 10입니다.'B'에서 정의 된 메소드의 경우 5. 내부 클래스,'i'가 가장 가까운 정의 : 로컬 변수, 같은 클래스의 필드, 수퍼 클래스의 필드 (순서대로). –

2

Java 개발자가 설계 한 내용이며 Java Language Specification에 설명되어 있습니다.

부모 클래스의 메서드 overrides에있는 메서드와 동일한 메서드 서명을 사용하는 메서드입니다.

부모 클래스의 변수와 동일한 이름의 변수 hides 부모 변수입니다.

차이점은 변수를 부모 형식으로 캐스팅하면 숨겨진 값에 액세스 할 수 있지만 재정의 된 메서드는 항상 자식 클래스의 메서드를 실행합니다.

다른 사람들은 C++과 C#에서 Java와 동일한 대체 동작을 얻으려는 경우, 메소드를 가상으로 선언해야한다고 지적했습니다.

-1

팁 : setter 및 getters를 사용하여 사용하는 데이터 멤버를 확인할 수 있습니다.
또는 클래스 선언 대신 생성자에서 값을 설정하기 만하면됩니다.

3

왜 Java에서 필드가 대체되지 않습니까?

자바에서 인스턴스 필드 조회가 컴파일 타임에 발생하기 때문에 자바는 객체 메모리의 주어진 오프셋에서 필드의 값을 제공합니다 (컴파일 할 때의 유형 정보를 기반으로합니다.이 경우에는 a가 선언됩니다). A 형이 됨).

void foo() { 
    A a = new B(); 
    int val = a.i; // compiler uses type A to compute the field offset 
} 

한 요청할 수있다 "는 aB의 인스턴스가 실제로는 것을 알고 있기 때문에 왜 사용 유형 B을 컴파일러하지 않았다? 그냥 위의 할당에서 명확하지가?". 물론 위의 경우에는 비교적 명확하며 컴파일러는 더 똑똑해지기 위해 노력할 수 있습니다.

하지만 그 코드의 "난이도"조각은과 같이, 발생하면 어떤 컴파일러 디자인 "쥐 구멍"입니다 : 컴파일러 라면

void foo(A a) { 
    int val = a.i; 
} 

는 "스마트", 그것은 그것의 일이 될 것입니다 foo()의 모든 호출을보고 실제 유형이 사용되었는지 확인하십시오. 컴파일러는 알 수 없거나 아직 작성되지 않은 호출자가 foo()에 전달할 수있는 다른 미친 것들을 예측할 수 없으므로 불가능한 작업입니다.

+1

어쨌든 똑똑한 코딩 (즉 int getI() {return i;})으로 문제가 해결됩니다. – Zed

관련 문제