“Child x = new Child ();”대신“Parent x = new Child ();”가 후자를 사용할 수 있다면 나쁜 습관입니까?


32

예를 들어, 다음과 같은 조각을 만드는 코드를 보았습니다.

Fragment myFragment=new MyFragment();

MyFragment 대신 변수를 Fragment로 선언합니다. MyFragment는 Fragment의 자식 클래스입니다. 이 코드는 다음과 같아야한다고 생각하기 때문에이 코드 줄을 만족시키지 못했습니다.

MyFragment myFragment=new MyFragment();

어느 것이 더 구체적입니까?

또는 질문의 일반화에서 사용하는 것이 나쁜 습관입니까?

Parent x=new Child();

대신에

Child x=new Child();

컴파일 오류없이 전자를 후자로 바꿀 수 있다면?


6
우리가 후자를한다면, 더 이상 추상화 / 일반화의 여지가 없습니다. 물론 예외가 있습니다 ...
Walfrat

2
내가 작업중 인 프로젝트에서 내 함수는 부모 유형을 기대하고 부모 유형 메소드를 실행합니다. 여러 자식 중 하나를 수락 할 수 있으며 해당 메서드 (상속되고 재정의 된) 메서드의 코드는 다르지만 사용중인 자식에 관계없이 해당 코드를 실행하고 싶습니다.
Stephen S

4
@Walfrat 실제로 구체적인 유형을 인스턴스화하는 경우 추상화의 요점이 거의 없지만 여전히 클라이언트 코드가 행복하게 무의식적으로되도록 추상 유형을 반환 할 수 있습니다 .
Jacob Raihle

4
부모 / 자식을 사용하지 마십시오. 인터페이스 / 클래스 (Java 용)를 사용하십시오.
Thorbjørn Ravn Andersen 2016 년

4
사용 Parent x = new Child()하는 것이 좋은 아이디어 인 이유에 대한 설명은 Google "인터페이스 프로그래밍"입니다 .
Kevin Workman

답변:


69

상황에 따라 다르지만 가능한 가장 추상적 인 유형을 선언해야한다고 주장 합니다. 그렇게하면 코드가 가능한 한 일반적이며 관련이없는 세부 사항에 의존하지 않습니다.

예는를 갖는 것 LinkedListArrayList이로부터 양쪽이 하강 List. 코드가 어떤 종류의 목록에서도 똑같이 잘 작동한다면 임의의 하위 클래스 중 하나로 코드를 제한 할 이유가 없습니다.


24
정확하게. 예제를 추가하려면에 대해 생각해보십시오 List list = new ArrayList(). 목록을 사용하는 코드는 목록의 종류를 신경 쓰지 않고 List 인터페이스의 계약을 충족시키는 것만 고려합니다. 앞으로는 다른 List 구현으로 쉽게 변경할 수 있으며 기본 코드는 전혀 변경하거나 업데이트 할 필요가 없습니다.
Maybe_Factor

19
좋은 대답이지만 가능한 가장 추상적 인 유형을 확장해야 함은 범위의 코드가 일부 자식 특정 작업을 수행하기 위해 자식으로 다시 캐스팅해서는 안됨을 의미합니다.
Mike

10
@ Mike : 말할 것도없이 모든 변수, 매개 변수 및 필드가로 선언되기를 바랍니다 Object.
JacquesB

3
@JacquesB :이 질문과 답변이 많은 주목을 받았기 때문에, 명시적인 것이 모든 다른 기술 수준의 사람들에게 유용 할 것이라고 생각했습니다.
Mike

1
정답이 아닌 상황에 따라 다릅니다 .
Billal Begueradj

11

JacquesB의 대답은 약간 추상적이지만 정확합니다. 몇 가지 측면을 더 명확하게 강조하겠습니다.

코드 스 니펫에서 new키워드 를 명시 적으로 사용하고 구체적 유형에 따라 추가 초기화 단계를 계속하고 싶을 때 해당 구체적인 유형으로 변수를 선언해야합니다.

예를 들어 다른 곳에서 해당 항목을 가져 오면 상황이 다릅니다 IFragment fragment = SomeFactory.GiveMeFragments(...);. 여기서 팩토리는 일반적으로 가능한 가장 추상적 인 유형을 반환해야하며 하향 코드는 구현 세부 사항에 의존해서는 안됩니다.


18
"약간 추상적이지만". Badumtish.
rosuav 2016 년

1
이 편집은 할 수 없습니까?
beppe9000

6

잠재적으로 성능 차이가 있습니다.

파생 클래스가 봉인 된 경우 컴파일러는 가상 호출을 인라인하고 최적화하거나 제거 할 수 있습니다. 따라서 상당한 성능 차이가있을 수 있습니다.

예 : ToString가상 통화이지만 String봉인되어 있습니다. 에 String, ToString당신이 선언 그렇다면, 아무 조합입니다 object즉 가상 전화의 당신이 선언하는 경우, String클래스가 봉인되어 있기 때문에 컴파일러가 알고있는 어떤 파생 클래스 메소드를 오버라이드 (override) 한 즉 아무 조합 없습니다 그래서. ArrayListvs에 유사한 고려 사항이 적용됩니다 LinkedList.

따라서 객체의 구체적인 유형을 알고 있고 숨길 캡슐화 이유가없는 경우 해당 유형으로 선언해야합니다. 방금 객체를 만들었으므로 콘크리트 유형을 알고 있습니다.


4
대부분의 경우 성능 차이는 미미합니다. 그리고 대부분의 경우 (이것이 시간의 약 90 %라고 들었습니다) 우리는 그 작은 차이를 잊어야합니다. 코드의 성능 결정 섹션으로 노화되는 것을 알고있는 경우 (바람직하게는 측정을 통해) 그러한 최적화에 관심을 가져야합니다.
Jules

그리고 컴파일러는 "new Child ()"가 Parent에 할당 된 경우에도 Child를 반환한다는 것을 알고 있으며 알고있는 한 해당 지식을 사용하여 Child () 코드를 호출 할 수 있습니다.
gnasher729

+1. 또한 내 대답에서 당신의 모범을 훔쳤습니다. = P
Nat

1
모든 것을 무질서하게 만드는 성능 최적화가 있습니다. 예를 들어, HotSpot은 함수에 전달 된 매개 변수가 항상 하나의 특정 클래스이며 그에 따라 최적화됨을 알 수 있습니다. 그 가정이 거짓으로 판명되면 그 방법은 최적화되지 않은 것입니다. 그러나 이것은 컴파일러 / 런타임 환경에 크게 의존합니다. 지난번에 저는 CLR이 p Parent p = new Child()가 항상 자식 이라는 것을 알아 차리는 사소한 최적화조차 할 수 없었 음을 확인했습니다 .
Voo

4

중요한 차이점은 필요한 액세스 수준입니다. 그리고 추상화에 대한 모든 좋은 대답은 동물과 관련이 있습니다.

- 우리는 당신이 몇 가지 동물을 가정하자 Cat BirdDog. 이제,이 동물은 몇 가지 일반적인 행동이 - move(), eat()하고 speak(). 그들은 모두 다르게 먹고 다르게 말하지만, 동물이 먹거나 말해야한다면 어떻게하는지 신경 쓰지 않습니다.

그러나 때때로 나는한다. 난 가드캣이나 가드 버드를 세게 한 적이 없지만 가드 독이있다. 누군가가 내 집에 침입했을 때, 나는 침입자를 놀라게하기 위해 말을 할 수 없습니다. 나는 짖을 개가 정말로 필요하다. 이것은 그의 말과는 다르다.

따라서 침입자가 필요한 코드에서는 실제로해야합니다. Dog fido = getDog(); fido.barkMenancingly();

그러나 대부분의 경우, 동물이 간식을 먹기 위해 야옹이나 짹짹을 할 수있는 트릭을 기뻐할 수 있습니다. Animal pet = getAnimal(); pet.speak();

그래서 더 구체적으로, ArrayList를이 가지고 몇 가지 방법 List하지 않습니다. 특히 trimToSize(). 현재 99 %의 사례에서는이 점에 신경 쓰지 않습니다. 는 List우리가 걱정하는 거의 결코 큰 충분하다. 그러나 때가 있습니다. 따라서 때때로 ArrayList실행을 요청 trimToSize()하고 백업 배열을 더 작게 만들어야합니다.

이것은 시공시에 이루어질 필요는 없습니다. 절대적으로 확실하다면 반환 방법이나 캐스트를 통해 수행 할 수 있습니다.


이것이 공감대를 얻는 이유가 혼란 스럽습니다. 개인적으로 정식 인 것 같습니다. 프로그램 가능한
보호견

최상의 답변을 얻기 위해 왜 페이지를 읽어야합니까? 평가는 짜증나.
Basilevs

친절한 말 감사합니다. 등급 시스템에는 장단점이 있으며, 그 중 하나는 나중에 답변이 때때로 먼지에 남을 수 있다는 것입니다. 일어난다!
corsiKa

2

일반적으로 말해서

var x = new Child(); // C#

또는

auto x = new Child(); // C++

변수가 초기화 된 것과 다른 유형을 갖는 적절한 이유가없는 한 로컬 변수의 경우.

(여기서는 C ++ 코드가 스마트 포인터를 사용해야한다는 사실을 무시하고 있습니다. 실제로 알 수없는 두 줄 이상이 있거나없는 경우가 있습니다.)

여기서 일반적인 아이디어는 변수의 자동 유형 감지를 지원하는 언어를 사용하여 코드를 더 쉽게 읽고 올바른 작업을 수행하는 것입니다 (즉, 컴파일러의 유형 시스템은 최대한 최적화하고 초기화를 새로운 것으로 변경 함) 대부분의 초록이 사용 된 경우에도 형식이 작동합니다).


1
C ++에서 변수는 주로 새로운 키워드없이 생성됩니다 stackoverflow.com/questions/6500313/...
마리아 Spanik

1
문제는 C # / C ++에 관한 것이 아니라 최소한 정적 형식의 형식이있는 언어의 일반적인 객체 지향 문제에 관한 것입니다 (즉, Ruby와 같은 방식으로 물어 보는 것은 무의미합니다). 게다가, 그 두 언어로도, 우리가 왜 당신이 말한 것처럼해야하는지에 대한 충분한 이유가 없습니다.
AnoE

1

이 코드는 이해하기 쉽고 수정하기 쉽기 때문에 좋습니다.

var x = new Child(); 
x.DoSomething();

의도를 전달하기 때문에 좋습니다.

Parent x = new Child(); 
x.DoSomething(); 

일반적이고 이해하기 쉽기 때문에 좋습니다.

Child x = new Child(); 
x.DoSomething(); 

정말 나쁜 방법 중 하나 는 방법이있는 Parent경우에만 사용 Child하는 것 DoSomething()입니다. 의도를 잘못 전달하기 때문에 좋지 않습니다.

Parent x = new Child(); 
(x as Child).DoSomething(); // DON'T DO THIS! IF YOU WANT x AS CHILD, STORE x AS CHILD

이제 질문이 구체적으로 요구하는 경우에 대해 좀 더 자세히 설명하겠습니다.

Parent x = new Child(); 
x.DoSomething(); 

후반을 함수 호출로 만들어서 다르게 형식화 해 봅시다 :

WhichType x = new Child(); 
FunctionCall(x);

void FunctionCall(WhichType x)
    x.DoSomething(); 

이것은 다음과 같이 단축 될 수 있습니다.

FunctionCall(new Child());

void FunctionCall(WhichType x)
    x.DoSomething();

여기서는 WhichType성능 문제가없는 한 함수가 작동 할 수있는 가장 기본 적이고 추상적 인 유형이어야한다는 것이 널리 받아 들여지고 있다고 가정합니다 . 이 경우 적절한 유형은 Parent이거나 무언가 Parent에서 파생됩니다.

이 추론은 왜 유형을 사용 Parent하는 것이 좋은 선택 인지 설명 하지만 날씨가 좋지 않아 다른 선택은 나쁘다 (그렇지 않다).


1

TL; DR - 사용Child오버Parent지역 범위가 바람직하다. 가독성을 높이는 데 도움이 될뿐만 아니라 오버로드 된 분석법 해결이 제대로 작동하고 효율적인 컴파일이 가능하도록해야합니다.


로컬 범위에서

Parent obj = new Child();  // Works
Child  obj = new Child();  // Better
var    obj = new Child();  // Best

개념적으로 가능한 가장 많은 유형 정보를 유지하는 것입니다. 로 다운 그레이드하면 Parent기본적으로 유용 할 수있는 유형 정보를 제거합니다.

완전한 유형 정보를 유지하면 다음과 같은 네 가지 주요 이점이 있습니다.

  1. 컴파일러에 자세한 정보를 제공합니다.
  2. 독자에게 더 많은 정보를 제공합니다.
  3. 보다 깨끗하고 표준화 된 코드.
  4. 프로그램 논리를 더 변경 가능하게 만듭니다.

장점 1 : 컴파일러에 대한 추가 정보

명백한 유형은 오버로드 된 분석법 해결 및 최적화에 사용됩니다.

예 : 과부하 된 분석법 해결

main()
{
    Parent parent = new Child();
    foo(parent);

    Child  child  = new Child();
    foo(child);
}
foo(Parent arg) { /* ... */ }  // More general
foo(Child  arg) { /* ... */ }  // Case-specific optimizations

위의 예제에서 두 foo()호출 은 모두 작동 하지만 어떤 경우에는 오버로드 된 메소드 분석이 더 좋습니다.

예 : 컴파일러 최적화

main()
{
    Parent parent = new Child();
    var x = parent.Foo();

    Child  child  = new Child();
    var y = child .Foo();
}
class Parent
{
    virtual         int Foo() { return 1; }
}
class Child : Parent
{
    sealed override int Foo() { return 2; }
}

위의 예제에서 두 .Foo()호출은 결국 override을 반환 하는 동일한 메소드를 호출 합니다 2. 첫 번째 경우에는 올바른 방법을 찾기위한 가상 방법 검색이 있습니다. 이 가상 메소드 조회는 해당 메소드 이후의 두 번째 경우에는 필요하지 않습니다 sealed.

그의 대답에 비슷한 예를 제공 한 @Ben에게 감사의 말을 전한다 .

장점 2 : 독자에게 더 많은 정보

정확한 유형을 알고 Child있으면 (예 :) 코드를 읽는 사람에게 더 많은 정보를 제공하여 프로그램이 수행하는 작업을 쉽게 확인할 수 있습니다.

물론, 어쩌면 모두 이후 실제 코드에 중요하지 않습니다 parent.Foo();child.Foo();메이크업 감각, 그러나 처음으로 코드를 보는 사람, 자세한 내용은 그냥 일반 도움이됩니다.

또한, 개발 환경에 따라 IDE에 대한 추가 도움이 툴팁 및 메타 데이터를 제공 할 수 있습니다 Child보다 Parent.

장점 3 :보다 깨끗하고 표준화 된 코드

최근에 보았던 C # 코드 예제의 대부분 var은 기본적으로 축약 형입니다 Child.

Parent obj = new Child();  // Sub-optimal
Child  obj = new Child();  // Optimal, but anti-pattern syntax
var    obj = new Child();  // Optimal, clean, patterned syntax "everyone" uses now

var선언 이 아닌 진술을 보는 것은 단지 외모입니다. 상황에 맞는 이유가 있다면 대단하지만 그렇지 않으면 반 패턴으로 보입니다.

// Clean:
var foo1 = new Person();
var foo2 = new Job();
var foo3 = new Residence();

// Staggered:
Person foo1 = new Person();
Job foo2 = new Job();
Residence foo3 = new Residence();   

장점 4 : 프로토 타이핑을위한 더 가변적 인 프로그램 로직

처음 세 가지 장점은 큰 장점이었습니다. 이것은 훨씬 더 상황이 좋지 않습니다.

아직도, 다른 사람들이 Excel을 사용하는 것처럼 코드를 사용하는 사람들을 위해 우리는 끊임없이 코드를 변경하고 있습니다. 버전의 코드 Child에서 고유 한 메소드를 호출 할 필요는 없지만 나중에 코드의 용도를 변경하거나 재 작업 할 수 있습니다.

강력한 유형 시스템의 장점은 프로그램 논리에 대한 특정 메타 데이터를 제공하여 가능성을보다 쉽게 ​​알 수 있다는 것입니다. 이것은 프로토 타이핑에 매우 유용하므로 가능하면 유지하는 것이 가장 좋습니다.

개요

사용 Parent과부하 방법 해상도 엉망을 일부 컴파일러 최적화 억제 리더의 정보를 제거하고 코드 못 생겼다고한다.

사용하는 var것은 실제로 갈 길입니다. 빠르고 깨끗하며 패턴 화되어 있으며 컴파일러와 IDE가 작업을 올바르게 수행하도록 도와줍니다.


중요 :이 답변에 관한ParentChild방법의 로컬 범위이다. Parentvs.의 문제는Child반환 유형, 인수 및 클래스 필드에서 매우 다릅니다.


2
나는 Parent list = new Child()그다지 이해가되지 않는 것을 추가 할 것 입니다. 팩토리, 팩토리 메소드 등을 통한 간접적 인 반대와 같이 오브젝트의 구체적인 인스턴스 를 직접 작성 하는 코드 에는 작성중인 유형에 대한 완벽한 정보가 있으며, 새로 작성된 오브젝트 등을 구성하려면 구체적 인터페이스와 상호 작용해야 할 수도 있습니다. 로컬 범위는 유연성을 확보 할 수있는 곳 이 아닙니다 . 보다 추상적 인 인터페이스를 사용하여 새로 작성된 객체를 클래스의 클라이언트에 노출하면 유연성이 달성됩니다 (공장 및 팩토리 메소드가 완벽한 예)
crizzis

"tl; dr 로컬 범위에서는 Child over Child를 사용하는 것이 바람직합니다. 가독성을 높이는 데 도움이 될뿐만 아니라 반드시 ... 자식 세부 사항을 사용하지 않으면 필요한 것보다 자세한 내용을 추가해야합니다. "하지만 오버로드 된 메소드 분석이 올바르게 작동하고 효율적인 컴파일을 가능하게하는 것도 보장해야합니다." -그것은 프로그래밍 언어에 많이 의존합니다. 이것으로 인해 발생하는 문제는 없습니다.
AnoE

1
소스가 Parent p = new Child(); p.doSomething();있고 Child c = new Child; c.doSomething();다른 동작을합니까? 어쩌면 그것은 어떤 언어의 기발한 것일지도 모르지만, 객체는 참조가 아닌 행동을 제어해야합니다. 그것은 상속의 아름다움입니다! 부모 구현의 계약을 이행하기 위해 자식 구현을 신뢰할 수있는 한 좋습니다.
corsiKa

@corsiKa 네, 몇 가지 방법으로 가능합니다. 예를 들어 newC #키워드를 사용하여 메소드 서명을 덮어 쓰는 경우와 C ++의 다중 상속을 사용 하여 여러 정의 가있는 경우 두 가지 간단한 예를들 수 있습니다 .
Nat

@corsiKa 간단한 세 번째 예 (C ++과 C #의 깔끔한 대조라고 생각하기 때문에) C #에는 C ++ 과 같은 문제가 있으며 명시 적 인터페이스 메소드 에서이 동작을 얻을 수 있습니다 . C #과 같은 언어는 이러한 문제를 피하기 위해 부분적으로 다중 상속 대신 인터페이스를 사용하기 때문에 재미 있지만 인터페이스를 채택하면 여전히 문제의 일부가 발생하지만 그 시나리오에서는 적용되지 않기 때문에 나쁘지는 않습니다. 때까지 Parent입니다 interface.
Nat

0

주어진 parentType foo = new childType1();코드는 부모 유형 메소드를 사용하는 것으로 제한 foo되지만의 인스턴스뿐만 아니라 foo유형에서 파생되는 모든 객체 에 대한 참조에 저장할 수 있습니다 . 새로운 인스턴스가 참조를 보유 할 유일한 것이라면, 타입을로 선언하는 것이 좋습니다 . 반면 에 다른 유형에 대한 참조를 보유해야하는 경우 이를 가능하게 선언했습니다 .parentchildType1childType1foofoochildType1fooparentType


0

나는 사용하는 것이 좋습니다

Child x = new Child();

대신에

Parent x = new Child();

전자는 정보를 잃지 만 전자는 정보를 잃습니다.

전자는 후자로는 할 수 있지만 그 반대는 할 수없는 모든 것을 할 수 있습니다.


포인터를 더 자세히 설명하기 위해 여러 수준의 상속을하자.

Base-> DerivedL1->DerivedL2

그리고 new DerivedL2()변수 의 결과를 초기화하려고 합니다. 기본 유형을 사용하는 것은 당신에게 사용할 수있는 옵션 잎 Base또는 DerivedL1.

당신이 사용할 수있는

Base x = new DerivedL2();

또는

DerivedL1 x = new DerivedL2();

나는 다른 것을 선호하는 논리적 인 방법을 보지 못했습니다.

당신이 사용하는 경우

DerivedL2 x = new DerivedL2();

논쟁 할 것이 없습니다.


3
더 많은 일을 할 필요가 없다면 더 적은 일을 할 수 있다는 것이 좋은 일입니다.
Peter

1
@ 피터, 적은 능력은 제한되지 않습니다. 항상 가능합니다. 더 많은 일을 할 수있는 능력이 제한된다면 아픈 일이 될 수 있습니다.
R Sahu

그러나 정보를 잃는 것은 때때로 좋은 일입니다. 계층 구조와 관련하여 대부분의 사람들이 투표 할 것입니다 Base(작동한다고 가정). AFAIK가 가장 구체적이며 가장 덜 선호합니다. 지역 변수의 경우 거의 중요하지 않다고 말하고 싶습니다.
maaartinus

@maaartinus, 대부분의 사용자가 정보를 잃는 것을 선호한다는 사실에 놀랐습니다. 나는 다른 사람들처럼 정보를 잃어버린 것의 이점을보고 있지 않습니다.
R Sahu

당신이 선언 Base x = new DerivedL2();하면 당신은 가장 제한되어 있으며 이것은 최소한의 노력으로 다른 (손자) 자녀를 바꿀 수 있습니다. 또한 가능하다는 것을 즉시 알 수 있습니다. +++ 그것 void f(Base)보다 낫다 는 데 동의 void f(DerivedL2)합니까?
maaartinus

0

항상 변수를 가장 구체적인 유형으로 반환하거나 저장하지만 매개 변수를 가장 넓은 유형으로 허용하는 것이 좋습니다.

예 :

<K, V> LinkedHashMap<K, V> keyValuePairsToMap(List<K> keys, List<V> values) {
   //...
}

매개 변수는 List<T>매우 일반적인 유형입니다. ArrayList<T>, LinkedList<T>등은 모두이 방법에 의해 허용 될 수있다.

중요한 것은 반환 유형이 LinkedHashMap<K, V>아니라 Map<K, V>입니다. 누군가이 방법의 결과를에 할당하고 싶다면 Map<K, V>그렇게 할 수 있습니다. 이 결과는 단순한지도가 아니라 순서가 정의 된지도라는 것을 분명히 알려줍니다.

이 메소드가 돌려 주었을 경우 Map<K, V>. 호출자가를 원한다면 LinkedHashMap<K, V>유형 검사, 캐스트 및 오류 처리를 수행해야하는데 이는 실제로 성가시다.


매개 변수로 확장 유형을 강제로 적용하는 동일한 논리가 리턴 유형에도 적용되어야합니다. 함수의 목표는 키를 값에 매핑하는 경우, 그것은 반환해야 Map하지 않는 LinkedHashMap- 당신은 단지를 반환 할 것입니다 LinkedHashMap유사한 기능을 위해 keyValuePairsToFifoMap또는 뭔가. 특별한 이유가 없을 때까지 더 넓을수록 좋습니다.
corsiKa

@corsiKa 흠 나는 그것이 더 나은 이름이라는 데 동의하지만 이것은 단지 하나의 예일뿐입니다. 귀하의 반품 유형을 넓히는 것이 더 일반적이라는 데 동의하지 않습니다. 정당화없이 인위적으로 유형 정보를 제거합니다. 사용자가 제공 한 광범위한 유형을 합리적으로 캐스트해야한다고 생각하면 서비스를 중단 한 것입니다.
알렉산더-복원 모니카

"어떤 정당화없이"-그러나 정당화가 있습니다! 더 넓은 유형을 반환하여 도망 갈 수 있다면 리팩토링이 더 쉬워집니다. 누군가 특정 유형의 동작으로 인해 특정 유형이 필요한 경우 적절한 유형을 반환해야합니다. 그러나 내가 필요하지 않은 경우 특정 유형에 갇히고 싶지 않습니다. 나는 그것을 소비하는 사람들을 해치지 않고 내 방법의 세부 사항을 변경 자유롭게, 그리고 내가에서를 변경하는 경우,라고 말하고 싶지만 LinkedHashMap으로 TreeMap지금은 변화에 물건을 많이 가지고, 어떤 이유.
corsiKa

사실, 나는 마지막 문장까지 그 모든 것에 동의합니다. 당신은 변경 LinkedHashMapTreeMap같은에서 변경 등의 사소한 변경되지 않습니다 ArrayListLinkedList. 의미 상 중요한 변화입니다. 이 경우 당신이 원하는 컴파일러가 변경 사항을 통지하고 적절한 조치를 취할 반환 값의 소비자를 강제로 오류가 발생 할 수 있습니다.
알렉산더-복원 모니카

그러나 당신이 관심을 갖는 모든 것이지도 행동이라면 가능하다면 사소한 변화 가 될 수 있습니다 ( 가능한 경우, 당신은해야합니다.) 트리 또는 해시 맵인지 여부는 중요하지 않습니다. 예를 들어, Thread#getAllStackTraces()- 도로 아래에서 구체적 으로 Map<Thread,StackTraceElement[]>대신에 HashMap원하는 유형으로 변경할 수 있습니다 Map.
corsiKa
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.