문제

이것은 권투와 Unboxing이 무엇인지에 대한 문제가 아닙니다. Java 및 C#과 같은 언어에 이것이 필요합니까?

저는 C++, STL 및 Boost에 대해 매우 잘 알고 있습니다.

C++에서는 이와 같은 것을 매우 쉽게 작성할 수 있습니다.

std::vector<double> dummy;

Java를 사용해 본 경험이 있지만 이런 것을 작성해야 해서 정말 놀랐습니다.

ArrayList<Double> dummy = new ArrayList<Double>();

내 질문은, 왜 객체여야 하는지, Generics에 대해 이야기할 때 기본 유형을 포함하는 것이 기술적으로 그렇게 어려운 이유는 무엇입니까?

도움이 되었습니까?

해결책

제네릭에 대해 이야기 할 때 원시 유형을 포함하기가 기술적으로는 무엇이 어려운가?

Java의 경우 제네릭이 작동하는 방식 때문입니다. Java에서 제네릭은 컴파일 타임 트릭으로, Image 객체에 물체 ArrayList<String>. 그러나 Java의 제네릭은 유형 삭제로 구현됩니다. 런타임 중에 제네릭 유형 정보가 손실됩니다. 제네릭이 Java의 삶에서 상당히 늦게 추가 되었기 때문에 이것은 호환성 이유를위한 것이 었습니다. 이것은 런타임, an을 의미합니다 ArrayList<String> 효과적으로입니다 ArrayList<Object> (또는 더 나은 : 그냥 ArrayList 그것은 기대하고 반환합니다 Object 모든 방법에서) 자동으로 캐스트되는 String 값을 검색 할 때.

하지만 그때부터 int 파생되지 않습니다 Object, 당신은 그것을 기대하는 배열리스트에 넣을 수 없습니다 (런타임에) Object 그리고 당신은 캐스팅 할 수 없습니다 Object 에게 int 어느 하나. 이것은 원시를 의미합니다 int 상속하는 유형으로 래핑해야합니다. Object, 처럼 Integer.

예를 들어 C#은 다르게 작동합니다. C#의 제네릭은 런타임에 시행되며 List<int>. C#의 복싱은 다음과 같은 값 유형을 저장할 때만 발생합니다. int 참조 유형 변수와 같은 object. 부터 int C#에서 Object C#, 쓰기 object obj = 2 완벽하게 유효하지만 int는 박스형이되며 컴파일러가 자동으로 수행됩니다 (NO Integer 참조 유형은 사용자 또는 무엇이든 노출됩니다).

다른 팁

권투 및 Unboxing은 언어 (C# 및 Java와 같은)가 메모리 할당 전략을 구현하는 방식에서 태어난 필수품입니다.

특정 유형은 스택에 및 힙에 할당됩니다. 스택-할당 된 유형을 힙합 유형으로 처리하기 위해서는 스택-합금 유형을 힙으로 이동시키는 데 권투가 필요합니다. Unboxing은 역 프로세스입니다.

C# 스택-알 로케이션 유형에서 호출됩니다 가치 유형 (예 : System.Int32 그리고 System.DateTime) 및 힙 올라당 유형이 호출됩니다 참조 유형 (예 : System.Stream 그리고 System.String).

경우에 따라 값 유형을 참조 유형과 같은 값 유형을 처리 할 수있는 것이 유리하지만 (반사는 하나의 예입니다) 대부분의 경우 복싱 및 Unboxing을 피하는 것이 가장 좋습니다.

나는 또한 원시인이 물체에서 물려받지 않기 때문이라고 생각합니다. 매개 변수로서 무엇이든 수용 할 수있는 방법이 있다고 가정 해 봅시다.

class Printer {
    public void print(Object o) {
        ...
    }
}

다음과 같이 간단한 원시적 값을 해당 방법에 전달해야 할 수도 있습니다.

printer.print(5);

5는 원시적이고 객체가 아니기 때문에 복싱/무 복싱 없이는 그렇게 할 수 있습니다. 이러한 기능을 가능하게하기 위해 각 원시 유형의 인쇄 방법을 과부하 할 수 있지만 통증입니다.

Java가 제네릭에서 원시 유형을 지원하지 않는 이유만 알려드릴 수 있습니다.

첫째, 자바가 기본형을 가져야 하는지에 대한 논의가 매번 제기될 때마다 이를 지원해야 한다는 문제가 있었습니다.물론 실제 질문에 대한 토론을 방해했습니다.

두 번째로 이를 포함하지 않는 주된 이유는 이전 버전과의 호환성을 원했기 때문에 제네릭을 인식하지 못하는 VM에서 수정되지 않은 채 실행될 수 있기 때문입니다.이 이전 버전과의 호환성/마이그레이션 호환성 이유는 이제 Collections API가 제네릭을 지원하고 동일하게 유지되지만 (제네릭을 도입했을 때 C#에서와 같이) 제네릭 인식 Collection API의 완전히 새로운 세트가 없는 이유이기도 합니다.

호환성은 ersure(컴파일 시 제거된 일반 유형 매개변수 정보)를 사용하여 수행되었으며 이는 Java에서 검사되지 않은 캐스트 경고가 너무 많이 나타나는 이유이기도 합니다.

구체화된 제네릭을 추가할 수도 있지만 그렇게 쉽지는 않습니다.제거하는 대신 유형 정보 추가 런타임을 추가하면 소스 및 바이너리 호환성이 손상되므로 작동하지 않습니다. 원시 유형을 계속 사용할 수 없으며 해당 메소드가 없기 때문에 기존 컴파일된 코드를 호출할 수 없습니다. ).

다른 접근 방식은 C#이 선택한 접근 방식입니다.위 참조

그리고 자동 박싱/언박싱은 비용이 너무 많이 들기 때문에 이 사용 사례에서는 지원되지 않았습니다.

자바 이론 및 실제:제네릭 문제

Java 및 C# (C ++와 달리)에서 모든 것이 객체를 확장하므로 ArrayList와 같은 컬렉션 클래스는 객체 나 그 후손 (기본적으로 무엇이든)를 보유 할 수 있습니다.

그러나 성능의 이유로, Java의 프리미티브 또는 C#의 가치 유형에 특별한 지위가 주어졌습니다. 그들은 대상이 아닙니다. (Java)와 같은 것을 할 수 없습니다.

 7.toString()

Tostring은 객체의 메소드이지만. 이 끄덕임을 성능에 연결하기 위해 동등한 객체가 만들어졌습니다. Autoboxing은 래퍼 클래스에 Primitive를 넣고 다시 꺼내어 코드를 더 읽을 수 있도록 보일러 플레이트 코드를 제거합니다.

C#의 값 유형과 객체의 차이는 더 회색입니다. 보다 여기 그들이 어떻게 다른지에 대해.

힙에 저장된 모든 비 건조 비 스트링 객체에는 8 또는 16 바이트 헤더 (32/64 비트 시스템의 크기)가 포함되어 있으며 해당 객체의 공개 및 개인 필드의 내용이 포함되어 있습니다. 배열 및 문자열에는 위의 헤더가 있으며 각 요소의 배열 길이와 크기 (및 차원 수, 각 추가 치수의 길이 등), 첫 번째 필드의 모든 필드를 정의하는 더 많은 바이트가 있습니다. 요소, 두 번째 필드 등. 객체에 대한 참조가 주어지면 시스템은 헤더를 쉽게 검사하고 어떤 유형인지 결정할 수 있습니다.

참조 유형 스토리지 위치는 힙에 저장된 객체를 고유하게 식별하는 4 또는 8 바이트 값을 보유하고 있습니다. 현재 구현에서는 그 값이 포인터이지만 "객체 ID"로 생각하는 것이 더 쉽고 의미 적으로 동등합니다.

값 유형 스토리지 위치는 값 유형 필드의 내용을 보유하지만 관련 헤더는 없습니다. 코드가 변수 유형을 선언하는 경우 Int32, 정보를 저장할 필요가 없습니다. Int32 그것이 무엇인지 말하십시오. 그 위치가 an을 보유하고 있다는 사실 Int32 프로그램의 일부로 효과적으로 저장되므로 위치 자체에 저장할 필요가 없습니다. 예를 들어, 각각 백만 개의 개체가있는 경우 큰 절약을 나타냅니다. Int32. 각 물체는 Int32 작동 할 수있는 클래스를 식별하는 헤더가 있습니다. 해당 클래스 코드의 한 사본이 백만 인스턴스 중 하나에서 작동 할 수 있으므로 필드가 Int32 코드의 일부는 해당 필드의 모든 분야에 대한 스토리지를 갖는 것보다 훨씬 효율적입니다.

복싱은 값 유형 저장 위치의 내용을 해당 특정 값 유형을 기대하지 않는 코드로 전달하도록 요청할 때 필요합니다. 알 수없는 유형의 객체를 기대하는 코드는 힙에 저장된 객체에 대한 참조를 수락 할 수 있습니다. 힙에 저장된 모든 객체에는 객체의 유형을 식별하는 헤더가 있으므로 코드는 해당 유형을 알아야 할 방식으로 객체를 사용할 필요가있을 때마다 해당 헤더를 사용할 수 있습니다.

.NET에서는 일반 클래스 및 메소드라고하는 것을 선언 할 수 있습니다. 그러한 각 선언은 자동으로 자신이 행동 할 것으로 예상되는 요새 유형의 대상을 제외하고 동일한 클래스 또는 방법의 패밀리를 자동으로 생성합니다. 하나가 통과하는 경우 Int32 일상에 DoSomething<T>(T param), 그것은 모든 유형의 모든 인스턴스 인 루틴 버전을 자동으로 생성합니다. T 효과적으로 대체됩니다 Int32. 해당 루틴의 버전은 모든 스토리지 위치가 유형으로 선언된다는 것을 알게됩니다. T 보유합니다 Int32, 루틴이 사용하기 위해 하드 코딩 된 경우와 마찬가지로 Int32 스토리지 위치는 해당 위치 자체에 유형 정보를 저장할 필요가 없습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top