Java에 제네릭이 재작성되지 않은 것을 왜 신경써야 합니까?
이것은 최근 인터뷰에서 후보자가 자바 언어에 추가하기를 바라는 질문으로 떠올랐습니다.Java가 제네릭스를 재현하지 못하는 것은 보통 골칫거리로 여겨지지만, 막상 밀어붙이면 후보자가 거기에 있었다면 어떤 일을 할 수 있었을지 실제로 말할 수 없었습니다.
하지 않은 체크)에서 허용되므로 (raw)로 수 .List<Integer>
를 들어) (''가) 포함되어 .String
s. 유형 정보가 재검증되면 이는 분명히 불가능해질 수 있습니다. 그러나 이것보다 더 많은 정보가 있을 것입니다!
사람들이 정말 하고 싶은 일의 예를 올릴 수 있을까?재인식된 제네릭스를 이용할 수 있다면?내 말은, 넌 분명히 그런 타입을 가질 수 있을 거야List
아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 네.
public <T> void foo(List<T> l) {
if (l.getGenericType() == Integer.class) {
//yeah baby! err, what now?
편집: 간단한 업데이트입니다.답변은 주로 합격의 필요성에 관한 것으로 보입니다.Class
를 들면, 「」( 「」)를 참조해 주세요.EnumSet.noneOf(TimeUnit.class)
불가능하다고 생각되는 것을 찾고 있었습니다.예를 들어 다음과 같습니다.
List<?> l1 = api.gimmeAList();
List<?> l2 = api.gimmeAnotherList();
if (l1.getGenericType().isAssignableFrom(l2.getGenericType())) {
l1.addAll(l2); //why on earth would I be doing this anyway?
가장 큰 고민은 여러 범용 타입에 걸친 여러 디스패치의 이점을 이용할 수 없다는 것입니다.다음 사항은 불가능하며, 이것이 최선의 솔루션이 될 수 있는 경우가 많습니다.
public void my_method(List<String> input) { ... }
public void my_method(List<Integer> input) { ... }
이러한 「니즈」를 접한 몇 번으로부터, 결론은 다음과 같습니다.
public class Foo<T> {
private T t;
public Foo() {
this.t = new T(); // Help?
}
}
은 CC#은 C#으로 동작합니다.T
에는 기본 생성자가 있습니다.에서 런타임 유형을 가져오고 에서 컨스트럭터를 가져올 수도 있습니다.
은 Java를 입니다.Class<T>
의론으로서
public class Foo<T> {
private T t;
public Foo(Class<T> cls) throws Exception {
this.t = cls.newInstance();
}
}
인수로 할 필요는 없습니다.입니다.위도 단순한 예에 불과합니다.try-catch
★★★★★★★★★★★★★★★★★」
다른 모든 일반 유형 구성체의 경우 약간의 반사를 통해 실제 유형을 쉽게 해결할 수 있습니다.다음 Q&A는 사용 사례와 가능성을 보여 줍니다.
타입 세이프티가 떠오른다.매개 변수 유형으로 다운캐스트하는 것은 재작성된 제네릭이 없으면 항상 안전하지 않습니다.
List<String> myFriends = new ArrayList();
myFriends.add("Alice");
getSession().put("friends", myFriends);
// later, elsewhere
List<Friend> myFriends = (List<Friend>) getSession().get("friends");
myFriends.add(new Friend("Bob")); // works like a charm!
// and so...
List<String> myFriends = (List<String>) getSession().get("friends");
for (String friend : myFriends) print(friend); // ClassCastException, wtf!?
또한 추상화는 최소한 유형 매개 변수에 대한 런타임 정보에 관심이 있는 추상화보다 덜 누출될 수 있습니다.현재 범용 파라미터의 유형에 대한 런타임 정보가 필요한 경우 이 정보를 전달해야 합니다.Class
에 따라 하는지 여부따라서 외부 인터페이스는 구현에 따라 달라집니다(파라미터에 대해 RTI를 사용하는지 여부).
코드로 범용 어레이를 작성할 수 있습니다.
public <T> static void DoStuff() {
T[] myArray = new T[42]; // No can do
}
이것은 오래된 질문이고, 많은 답변들이 있지만, 나는 기존의 답변들이 빗나갔다고 생각합니다.
"reated"는 단지 실제를 의미하고 보통 글자 지우기의 반대일 뿐입니다.
Java Generics와 관련된 큰 문제는 다음과 같습니다.
- 이 무시무시한 박스 처리 요건과 원시 유형과 참조 유형 간의 연결 끊김.이것은 리플리케이션이나 타입 삭제와는 직접 관계가 없습니다.C#/Scala에서 이 문제를 해결합니다.
- 자기 타입은 없습니다.JavaFX 8은 이러한 이유로 "빌더"를 삭제해야 했습니다.타이프 삭제와는 전혀 관계가 없습니다.C#에 대해서는 확실하지 않지만 Scala에 의해 이 문제가 해결되었습니다.
- 선언측 유형 분산이 없습니다.C# 4.0/Scala에는 이것이 있습니다.타이프 삭제와는 전혀 관계가 없습니다.
- 할 수
void method(List<A> l)
★★★★★★★★★★★★★★★★★」method(List<B> l)
은 활자 매우 이것은 활자 삭제에 의한 것이지만 매우 사소한 것입니다. - 런타임 유형 리플렉션을 지원하지 않습니다.이것은 활자 지우기의 핵심이다.컴파일 시에 프로그램 로직을 최대한 검증하고 증명하는 슈퍼 어드밴스 컴파일러를 좋아하는 경우, 가능한 한 리플렉션을 사용하지 않는 것이 좋습니다.이러한 타입 삭제는 불필요합니다.보다 패치하고 스크립트 형식의 동적 타입 프로그래밍을 좋아하며 가능한 한 올바른 논리를 증명하는 컴파일러에 관심이 없다면 더 나은 반영과 수정 타입 삭제가 중요합니다.
시리얼화는 리플리케이션과 함께 보다 간단합니다.저희가 원하는 건
deserialize(thingy, List<Integer>.class);
우리가 해야 할 일은
deserialize(thing, new TypeReference<List<Integer>>(){});
보기 흉하고 펑크하게 작동한다.
이런 말을 하면 정말 도움이 되는 경우도 있어요.
public <T> void doThings(List<T> thingy) {
if (T instanceof Q)
doCrazyness();
}
이런 것들은 자주 깨물지는 않지만, 그럴 때 깨물어요.
Java Genircs에 대한 나의 노출은 매우 제한적이며, 다른 답변들이 이미 언급한 점 외에도, Maurice Naftalin과 Philip Walder의 책 Java Generics and Collections에 설명된 시나리오가 있습니다. 여기서 재현된 제네릭스는 유용합니다.
유형은 다시 만들 수 없으므로 매개 변수화된 예외를 가질 수 없습니다.
예를 들어, 아래 형식의 선언은 유효하지 않습니다.
class ParametericException<T> extends Exception // compile error
이는 catch구가 느려진 예외가 지정된 유형과 일치하는지 여부를 체크하기 때문입니다.이 검사는 인스턴스 테스트에 의해 실행되는 검사와 동일하며 유형을 다시 만들 수 없으므로 위의 형식의 문은 유효하지 않습니다.
위의 코드가 유효하면 다음과 같은 방법으로 예외를 처리할 수 있습니다.
try {
throw new ParametericException<Integer>(42);
} catch (ParametericException<Integer> e) { // compile error
...
}
이 책에서는 Java 제네릭을 C++ 템플릿의 정의(확장)와 유사하게 정의하면 최적화 기회가 많아지기 때문에 보다 효율적인 구현이 이루어질 수 있다고 언급하고 있습니다.하지만 이것 이상의 설명은 없기 때문에, 아는 사람의 설명(점수)이 있으면 도움이 됩니다.
어레이를 재정립하면 제네릭과 함께 훨씬 더 잘 동작할 수 있을 것입니다.
jdbc 결과 세트를 반복기로 표시하는 래퍼가 있습니다(의존성 주입을 통해 테스트 데이터베이스 시작 작업을 훨씬 쉽게 유닛화할 수 있습니다).
는 API처럼 보입니다.Iterator<T>
여기서 T는 생성자의 문자열만 사용하여 구성할 수 있는 유형입니다.그런 다음 반복기는 sql 쿼리에서 반환되는 문자열을 확인한 후 유형 T의 생성자와 대조하려고 합니다.
제네릭스가 구현되는 현재 방식으로는 결과 집합에서 생성할 객체의 클래스도 전달해야 합니다.올바르게 이해했다면 제네릭스가 재작성된 경우 T.getClass()를 호출하여 컨스트럭터를 취득한 후 Class.newInstance()의 결과를 캐스트할 필요가 없습니다.이것이 훨씬 더 적절합니다.
기본적으로 어플리케이션 작성보다 API를 쉽게 작성할 수 있다고 생각합니다.왜냐하면 오브젝트에서 더 많은 것을 추론할 수 있기 때문에 설정이 덜 필요하기 때문입니다.주석은 수많은 구성 대신 스프링이나 xstream 등에서 사용되는 것을 보기 전까지는 주석의 의미를 이해하지 못했습니다.
한가지 좋은 점은 원시적인 (가치 있는) 타입의 복싱을 피하는 것이다.이는 다른 사용자가 제기한 어레이 불만사항과 관련이 있으며 메모리 사용이 제한되는 경우에는 실제로 큰 차이가 발생할 수 있습니다.
또한 매개 변수화된 유형을 성찰할 수 있는 것이 중요한 프레임워크를 작성할 때 몇 가지 유형의 문제가 있습니다.물론 이것은 클래스 오브젝트를 런타임에 전달함으로써 해결할 수 있지만, 이는 API를 흐리게 하고 프레임워크 사용자에게 추가적인 부담을 줍니다.
당신이 특별한 것을 성취할 수 있다는 것은 아닙니다.더 이해하기 쉬울 것이다.글자를 지우는 것은 초보자에게는 어려운 시기인 것 같습니다.그리고 최종적으로 컴파일러의 동작 방법에 대한 이해가 필요합니다.
제 생각에는 제네릭스는 여분의 캐스팅을 많이 덜어주는 엑스트라일 뿐입니다.
여기 있는 모든 답변이 놓치고 있는 것은 항상 골칫거리입니다.타입이 지워지기 때문에 범용 인터페이스를 두 번 상속할 수 없습니다.이것은, 인터페이스를 미세하게 하는 경우에 문제가 되는 일이 있습니다.
public interface Service<KEY,VALUE> {
VALUE get(KEY key);
}
public class PersonService implements Service<Long, Person>,
Service<String, Person> //Can not do!!
여기 오늘 깨달은 것이 있습니다.재검증 없이 범용 아이템의 다양한 리스트를 받아들이는 방법을 작성하면 발신자는 자신이 타이프 세이프하다고 생각할 수 있지만, 실수로 오래된 것을 통과하면 메서드가 파괴됩니다.
그런 일은 없을 것 같나?네, 그때까지...클래스를 데이터 유형으로 사용합니다.이 시점에서 발신자는 기꺼이 많은 클래스 오브젝트를 송신하지만, 단순한 오타가 있으면 T에 준거하지 않는 클래스 오브젝트가 송신되어 재해가 발생합니다.
(NB: 제가 여기서 실수를 했을 수도 있지만, "일반적인 것"을 검색해 보면, 위의 내용은 여러분이 예상한 대로인 것 같습니다.이것을 실제로 문제 삼는 것은 Class의 사용이라고 생각합니다.발신자는 그다지 주의하지 않는 것 같습니다:( )
예를 들어 Class 객체를 맵의 키로 사용하는 패러다임을 사용하고 있습니다(단순한 맵보다 복잡하지만 개념적으로는 그렇습니다).
예: 이것은 Java Generics에서 매우 잘 작동합니다(간단한 예).
public <T extends Component> Set<UUID> getEntitiesPossessingComponent( Class<T> componentType)
{
// find the entities that are mapped (somehow) from that class. Very type-safe
}
예를 들어 Java Generics에서 재인증하지 않으면 모든 "클래스" 개체를 사용할 수 있습니다.이전 코드의 작은 확장자에 불과합니다.
public <T extends Component> Set<UUID> getEntitiesPossessingComponents( Class<T>... componentType )
{
// find the entities that are mapped (somehow) to ALL of those classes
}
위의 방법들은 개별 프로젝트에서 수천 번 작성되어야 하기 때문에 사람의 실수 가능성이 높아집니다.디버깅 실수는 "재미없다"는 것을 증명하고 있습니다.저는 현재 대안을 찾고 있지만, 큰 희망은 없습니다.
언급URL : https://stackoverflow.com/questions/1927789/why-should-i-care-that-java-doesnt-have-reified-generics
'programing' 카테고리의 다른 글
로드밸런싱을 위한 올바른 MariaDB Galera jdbc URL 속성은 무엇입니까? (0) | 2022.10.20 |
---|---|
mysql dump - 일부 테이블 데이터를 제외합니다. (0) | 2022.10.20 |
두 날짜 사이의 일 수를 계산하는 방법 (0) | 2022.10.20 |
DBeaver를 사용하여 MariaDB에 연결할 수 없습니다. (0) | 2022.10.20 |
함수 호출에서 옵션 인수를 건너뛰려면 어떻게 해야 합니까? (0) | 2022.10.20 |