답변:
그들 사이에는 큰 차이가 있습니다. C ++에서는 제네릭 형식에 대한 클래스 나 인터페이스를 지정할 필요가 없습니다. 그렇기 때문에 타이핑이 느슨해지면 진정한 일반 함수와 클래스를 만들 수 있습니다.
template <typename T> T sum(T a, T b) { return a + b; }
위의 방법은 동일한 유형의 두 객체를 추가하며 "+"연산자를 사용할 수있는 모든 유형 T에 사용할 수 있습니다.
Java에서는 전달 된 객체에서 메소드를 호출하려면 다음과 같이 유형을 지정해야합니다.
<T extends Something> T sum(T a, T b) { return a.add ( b ); }
C ++에서 일반 함수 / 클래스는 헤더에서만 정의 할 수 있습니다. 컴파일러는 서로 다른 유형 (함수 호출)에 대해 다른 함수를 생성하기 때문입니다. 따라서 컴파일 속도가 느려집니다. Java에서 컴파일에는 큰 페널티가 없지만 Java는 런타임에 일반 유형이 지워지는 "삭제"라는 기술을 사용하므로 런타임에 Java가 실제로 호출됩니다 ...
Something sum(Something a, Something b) { return a.add ( b ); }
따라서 Java의 일반적인 프로그래밍은 실제로 유용하지 않으며 새로운 foreach 구문을 돕는 것은 약간의 구문 설탕 일뿐입니다.
편집 : 유용성에 대한 위의 의견은 젊은 자아에 의해 작성되었습니다. Java의 제네릭은 물론 형식 안전성을 지원합니다.
extends
하거나 super
. 답변이 잘못되었습니다
Java Generics는 C ++ 템플릿 과 크게 다릅니다.
기본적으로 C ++ 템플릿에서는 기본적으로 영광스러운 전 처리기 / 매크로 세트입니다 ( 참고 : 일부 사람들은 유추를 이해할 수 없기 때문에 템플릿 처리가 매크로라고 말하지 않습니다). Java에서는 기본적으로 객체의 상용구 캐스팅을 최소화하기 위해 구문 설탕입니다. 다음은 C ++ 템플릿과 Java 제네릭에 대한 꽤 괜찮은 소개 입니다.
이 점을 자세히 설명하려면 C ++ 템플릿을 사용할 때 기본적으로 #define
매크로 를 사용하는 것처럼 다른 코드 사본을 작성 합니다. 이를 통해 int
배열의 크기 등을 결정하는 템플릿 정의에 매개 변수 가있는 것과 같은 작업을 수행 할 수 있습니다 .
Java는 그런 식으로 작동하지 않습니다. Java에서 모든 객체는 java.lang.Object 에서 확장됩니다. 되므로 pre-Generics는 다음과 같은 코드를 작성합니다.
public class PhoneNumbers {
private Map phoneNumbers = new HashMap();
public String getPhoneNumber(String name) {
return (String)phoneNumbers.get(name);
}
...
}
모든 Java 콜렉션 유형이 Object를 기본 유형으로 사용했기 때문에 무엇이든 넣을 수 있기 때문입니다. Java 5는 다음과 같은 작업을 수행 할 수 있도록 제네릭을 추가하고 추가합니다.
public class PhoneNumbers {
private Map<String, String> phoneNumbers = new HashMap<String, String>();
public String getPhoneNumber(String name) {
return phoneNumbers.get(name);
}
...
}
그리고 그것은 모든 Java Generics입니다 : 객체를 캐스팅하기위한 래퍼. Java Generics가 정제되지 않았기 때문입니다. 그들은 유형 삭제를 사용합니다. 이 결정은 Java Generics가 너무 늦게 나왔기 때문에 이전 버전과의 호환성을 깨뜨리고 싶지 않았습니다 (a Map<String, String>
가 필요할 때마다 사용할 수 있음 Map
). 유형 삭제가 사용되지 않는 .Net / C #과 비교하면 모든 종류의 차이점이 발생합니다 (예 : 기본 유형 IEnumerable
및IEnumerable<T>
서로 아무 관계도지지 않습니다).
Java 5+ 컴파일러로 컴파일 된 제네릭을 사용하는 클래스는 JDK 1.4에서 사용할 수 있습니다 (Java 5+가 필요한 다른 기능이나 클래스를 사용하지 않는다고 가정).
그렇기 때문에 Java Generics를 구문 설탕 이라고합니다. 합니다.
그러나 제네릭을 수행하는 방법에 대한이 결정은 많은 영향을 미치므로 (최상의) Java 제네릭 FAQ 가 사람들이 Java 제네릭에 대해 가지고있는 많은 질문에 대답하기 위해 생겨났습니다.
C ++ 템플릿에는 Java Generics에없는 많은 기능이 있습니다.
기본 유형 인수 사용
예를 들면 다음과 같습니다.
template<class T, int i>
class Matrix {
int T[i][i];
...
}
Java는 제네릭 형식에서 기본 형식 인수를 사용할 수 없습니다.
기본 유형 인수를 사용합니다 . Java에서 놓칠 수있는 기능 중 하나이지만 이전 버전과의 호환성 이유가 있습니다.
예를 들면 다음과 같습니다.
public class ObservableList<T extends List> {
...
}
다른 인수를 가진 템플릿 호출은 실제로 다른 유형이라는 점을 강조해야합니다. 정적 멤버도 공유하지 않습니다. Java에서는 그렇지 않습니다.
제네릭과의 차이점 외에도 완전성을 위해 C ++과 Java (및 다른 것 )의 기본 비교가 있습니다. ) 있습니다.
또한 Thinking in Java를 제안 할 수도 있습니다 . C ++ 프로그래머로서 객체와 같은 많은 개념은 이미 제 2의 특성이지만 미묘한 차이점이 있으므로 부품을 훑어 보더라도 소개 텍스트를 갖는 것이 좋습니다.
Java를 배울 때 배우게 될 많은 것들이 모든 라이브러리 (JDK에 포함 된 표준 라이브러리)와 스프링과 같이 일반적으로 사용되는 것을 포함하는 비표준 라이브러리입니다. Java 구문은 C ++ 구문보다 더 장황하며 많은 C ++ 기능 (예 : 연산자 오버로드, 다중 상속, 소멸자 메커니즘 등)을 갖지 않지만 C ++의 하위 집합으로 만들어지는 것은 아닙니다.
Map map = new HashMap<String, String>
. 이전 JVM에 새 코드를 배포 할 수 있으며 바이트 코드의 유사성으로 인해 실행됩니다.
C ++에는 템플릿이 있습니다. Java에는 제네릭이 있는데 C ++ 템플릿과 비슷하지만 매우 다릅니다.
템플릿은 이름에서 알 수 있듯이 템플릿 매개 변수를 입력하여 형식이 안전한 코드를 생성하는 데 사용할 수있는 (기다리는 중 ...) 템플릿을 컴파일러에 제공하여 작동합니다.
제네릭은 내가 이해하는 것처럼 다른 방법으로 작동합니다. 유형 매개 변수는 컴파일러에서 사용하여 코드를 사용하여 코드가 안전한지 확인하지만 결과 코드는 전혀 유형없이 생성됩니다.
C ++ 템플릿은 정말 좋은 매크로 시스템으로, Java 제네릭은 자동으로 타입 캐스트를 생성하는 도구로 생각하십시오 .
const
. C ++의 객체 const
는 const
-ness가 캐스트 되지 않는 한 포인터를 통해 수정되지 않습니다 . 마찬가지로 Java에서 일반 유형으로 작성된 내재 된 캐스트는 유형 매개 변수가 코드의 어딘가에서 수동으로 캐스트되지 않는 한 "안전한"것으로 보장됩니다.
C ++ 템플릿의 또 다른 기능으로는 Java 제네릭에는없는 특수화 기능이 있습니다. 이를 통해 특정 유형에 대해 다른 구현을 가질 수 있습니다. 예를 들어 int에 대해 고도로 최적화 된 버전을 유지하면서도 나머지 유형에 대한 일반 버전을 유지할 수 있습니다. 또는 포인터 및 비 포인터 유형에 대해 다른 버전을 가질 수 있습니다. 포인터를 넘길 때 역 참조 된 객체를 조작하려는 경우에 유용합니다.
필립와 들러 (Philip Wadler)의 Maurice Naftalin의 Java Generics and Collections 에이 주제에 대한 훌륭한 설명이 있습니다. 나는이 책을 강력히 추천한다. 인용 :
Java의 제네릭은 C ++의 템플릿과 유사합니다. ... 구문은 의도적으로 유사하며 시맨틱은 의도적으로 다릅니다. ... 의미 적으로 Java 제네릭은 삭제에 의해 정의되며 C ++ 템플릿은 확장에 의해 정의됩니다.
(출처 : oreilly.com )
기본적으로 AFAIK, C ++ 템플릿은 각 유형에 대한 코드 사본을 생성하는 반면 Java 제네릭은 정확히 동일한 코드를 사용합니다.
예, 당신은 말할 수있는 C ++ 템플릿이 자바 일반에 해당한다는 개념 (더 제대로 자바 제네릭 개념 ++ C에 해당합니다 말을하는 것입니다하지만)
C ++의 템플릿 메커니즘에 익숙하다면 제네릭은 비슷하지만 유사성은 피상적이라고 생각할 수 있습니다. 제네릭은 각 전문 분야마다 새로운 클래스를 생성하지 않으며 "템플릿 메타 프로그래밍"을 허용하지도 않습니다.
보낸 사람 : Java Generics
C ++ 템플릿의 또 다른 장점은 전문화입니다.
template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }
Special sum(const Special& a, const Special& b) { return a.plus(b); }
이제 포인터로 sum을 호출하면 두 번째 메소드가 호출되고 포인터가 아닌 객체로 sum을 호출하면 첫 번째 메소드가 호출 sum
되고 Special
오브젝트를 사용하여 호출 하면 세 번째 메소드가 호출됩니다. 나는 이것이 Java로 가능하다고 생각하지 않습니다.
@Keith :
이 코드는 실제로 잘못되었으며 더 작은 글리치 ( template
생략, 특수화 구문이 다르게 보임)와는 별도로 , 부분 특수화 는 함수 템플리트에서 작동 하지 않으며 클래스 템플리트에서만 작동합니다. 그러나 코드는 일반 템플릿 오버로드를 사용하는 대신 부분 템플릿 전문화없이 작동합니다.
template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }
아래의 답변은 Cracking The Coding Interview Solutions 책에서 13 장까지입니다.
Java 제네릭의 구현은 "type erasure : '라는 아이디어에 기반을두고 있습니다.이 기술은 소스 코드가 JVM (Java Virtual Machine) 바이트 코드로 변환 될 때 매개 변수화 된 유형을 제거합니다. 예를 들어, 다음과 같은 Java 코드가 있다고 가정하십시오.
Vector<String> vector = new Vector<String>();
vector.add(new String("hello"));
String str = vector.get(0);
컴파일하는 동안이 코드는 다음과 같이 다시 작성됩니다.
Vector vector = new Vector();
vector.add(new String("hello"));
String str = (String) vector.get(0);
Java 제네릭의 사용은 실제로 우리의 기능에 대해 크게 변경되지 않았습니다. 그것은 조금 더 예쁘게 만들었습니다. 이러한 이유로, 자바 제네릭은 때때로 "구문 설탕 :"으로 불린다.
이것은 C ++과는 상당히 다릅니다. C ++에서 템플릿은 본질적으로 영광스러운 매크로 세트이며 컴파일러는 각 유형에 대한 템플릿 코드의 새 복사본을 만듭니다. 이것에 대한 증거는 MyClass 인스턴스가 MyClass와 정적 변수를 공유하지 않는다는 사실입니다. 그러나 MyClass의 Tow 인스턴스는 정적 변수를 공유합니다.
/*** MyClass.h ***/
template<class T> class MyClass {
public:
static int val;
MyClass(int v) { val v;}
};
/*** MyClass.cpp ***/
template<typename T>
int MyClass<T>::bar;
template class MyClass<Foo>;
template class MyClass<Bar>;
/*** main.cpp ***/
MyClass<Foo> * fool
MyClass<Foo> * foo2
MyClass<Bar> * barl
MyClass<Bar> * bar2
new MyClass<Foo>(10);
new MyClass<Foo>(15);
new MyClass<Bar>(20);
new MyClass<Bar>(35);
int fl fool->val; // will equal 15
int f2 foo2->val; // will equal 15
int bl barl->val; // will equal 35
int b2 bar2->val; // will equal 35
Java에서 정적 변수는 다른 유형 매개 변수에 관계없이 MyClass 인스턴스간에 공유됩니다.
Java 제네릭과 C ++ 템플릿에는 다른 많은 차이점이 있습니다. 여기에는 다음이 포함됩니다.
템플릿은 매크로 시스템 일뿐입니다. 구문 설탕. 실제 컴파일 전에 완전히 확장됩니다 (또는 적어도 컴파일러는 마치 마치 것처럼 작동합니다).
예:
우리가 두 가지 기능을 원한다고 가정 해 봅시다. 하나의 함수는 두 개의 시퀀스 (목록, 배열, 벡터 등)를 취하고 내부 곱을 반환합니다. 다른 함수는 길이를 가져 와서 해당 길이의 두 시퀀스를 생성하여 첫 번째 함수에 전달하고 결과를 반환합니다. 문제는 두 번째 함수에서 실수를해서이 두 함수의 길이가 같지 않다는 것입니다. 이 경우 경고를 위해 컴파일러가 필요합니다. 프로그램이 실행될 때가 아니라 컴파일 할 때가 아닙니다.
Java에서는 다음과 같이 할 수 있습니다.
import java.io.*;
interface ScalarProduct<A> {
public Integer scalarProduct(A second);
}
class Nil implements ScalarProduct<Nil>{
Nil(){}
public Integer scalarProduct(Nil second) {
return 0;
}
}
class Cons<A implements ScalarProduct<A>> implements ScalarProduct<Cons<A>>{
public Integer value;
public A tail;
Cons(Integer _value, A _tail) {
value = _value;
tail = _tail;
}
public Integer scalarProduct(Cons<A> second){
return value * second.value + tail.scalarProduct(second.tail);
}
}
class _Test{
public static Integer main(Integer n){
return _main(n, 0, new Nil(), new Nil());
}
public static <A implements ScalarProduct<A>>
Integer _main(Integer n, Integer i, A first, A second){
if (n == 0) {
return first.scalarProduct(second);
} else {
return _main(n-1, i+1,
new Cons<A>(2*i+1,first), new Cons<A>(i*i, second));
//the following line won't compile, it produces an error:
//return _main(n-1, i+1, first, new Cons<A>(i*i, second));
}
}
}
public class Test{
public static void main(String [] args){
System.out.print("Enter a number: ");
try {
BufferedReader is =
new BufferedReader(new InputStreamReader(System.in));
String line = is.readLine();
Integer val = Integer.parseInt(line);
System.out.println(_Test.main(val));
} catch (NumberFormatException ex) {
System.err.println("Not a valid number");
} catch (IOException e) {
System.err.println("Unexpected IO ERROR");
}
}
}
C #에서는 거의 같은 것을 쓸 수 있습니다. C ++로 다시 작성하면 템플릿의 무한 확장에 대해 불평하면서 컴파일되지 않습니다.
나는 askanydifference 를 인용하고 싶습니다 여기에 :
C ++과 Java의 주요 차이점은 플랫폼에 대한 의존성에 있습니다. C ++은 플랫폼 의존 언어 인 반면 Java는 플랫폼 독립적 언어입니다.
위의 진술은 C ++이 진정한 제네릭 형식을 제공 할 수있는 이유입니다. Java에는 엄격한 검사가 있으므로 C ++에서 허용하는 방식으로 제네릭을 사용할 수 없습니다.