문제

Java에는 제네릭이 있고 C++에는 다음과 같은 매우 강력한 프로그래밍 모델이 제공됩니다. template에스.그렇다면 C++과 Java 제네릭의 차이점은 무엇입니까?

도움이 되었습니까?

해결책

그들 사이에는 큰 차이가 있습니다.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의 제네릭은 유형 안전성에 도움이 됩니다.

다른 팁

자바 제네릭은 대규모로 C++ 템플릿과 다릅니다.

기본적으로 C++ 템플릿은 기본적으로 미화된 전처리기/매크로 세트(메모: 일부 사람들은 비유를 이해하지 못하는 것 같기 때문에 템플릿 처리가 매크로라고 말하는 것은 아닙니다.Java에서는 기본적으로 객체의 상용구 캐스팅을 최소화하기 위한 구문 설탕입니다.여기 꽤 괜찮은데 C++ 템플릿과 Java 제네릭 소개.

이 점을 자세히 설명하면 다음과 같습니다.C++ 템플릿을 사용하면 기본적으로 마치 템플릿을 사용한 것처럼 코드의 또 다른 복사본을 생성하게 됩니다. #define 매크로.이를 통해 다음과 같은 작업을 수행할 수 있습니다. int 배열 크기 등을 결정하는 템플릿 정의의 매개변수.

자바는 그런 식으로 작동하지 않습니다.Java에서는 모든 객체의 범위가 다음과 같습니다. java.lang.Object 따라서 Generic 이전 버전에서는 다음과 같은 코드를 작성합니다.

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가 너무 늦게 출시되어 이전 버전과의 호환성을 깨고 싶지 않았기 때문에 내려졌습니다. Map<String, String> 언제든지 사용할 수 있습니다. Map )이 요구됩니다.이를 유형 삭제가 사용되지 않는 .Net/C#과 비교하면 모든 종류의 차이점이 발생합니다(예:기본 유형을 사용할 수 있으며 IEnumerable 그리고 IEnumerable<T> 서로 아무런 관련이 없습니다).

그리고 Java 5+ 컴파일러로 컴파일된 제네릭을 사용하는 클래스는 JDK 1.4에서 사용할 수 있습니다(Java 5+가 필요한 다른 기능이나 클래스를 사용하지 않는다고 가정).

이것이 Java Generics가 호출되는 이유입니다. 구문 설탕.

그러나 제네릭을 수행하는 방법에 대한 이러한 결정은 엄청난 영향을 미치므로 (훌륭한) 자바 제네릭 FAQ 사람들이 Java Generics에 관해 갖고 있는 수많은 질문에 답하기 위해 탄생했습니다.

C++ 템플릿에는 Java Generics에 없는 여러 기능이 있습니다.

  • 기본 유형 인수 사용.

    예를 들어:

    template<class T, int i>
    class Matrix {
      int T[i][i];
      ...
    }
    

    Java는 제네릭에서 기본 유형 인수의 사용을 허용하지 않습니다.

  • 사용 기본 유형 인수, 이는 Java에서 제가 놓친 기능 중 하나이지만 이에 대한 이전 버전과의 호환성 이유가 있습니다.

  • Java에서는 인수 경계를 허용합니다.

예를 들어:

public class ObservableList<T extends List> {
  ...
}

서로 다른 인수를 사용한 템플릿 호출은 실제로는 서로 다른 유형이라는 점을 강조할 필요가 있습니다.정적 멤버도 공유하지 않습니다.Java에서는 그렇지 않습니다.

완전성을 위해 제네릭과의 차이점을 제외하고 다음은 다음과 같습니다. C++과 Java의 기본 비교 (그리고 다른 것).

그리고 제안할 수도 있어요 자바로 생각하기.C++ 프로그래머로서 객체와 같은 많은 개념은 이미 제2의 천성이겠지만 미묘한 차이점이 있으므로 부분을 훑어보더라도 소개 텍스트를 갖는 것이 가치가 있을 수 있습니다.

Java를 배울 때 배우게 되는 많은 내용은 모든 라이브러리입니다(JDK에 포함된 표준 라이브러리와 Spring과 같이 일반적으로 사용되는 라이브러리를 포함하는 비표준 라이브러리 모두).Java 구문은 C++ 구문보다 더 장황하며 C++ 기능이 많지 않습니다(예:연산자 오버로딩, 다중 상속, 소멸자 메커니즘 등) 그러나 이것이 엄밀히 말하면 C++의 하위 집합이 되는 것은 아닙니다.

C++에는 템플릿이 있습니다.Java에는 C++ 템플릿과 비슷해 보이지만 매우 다른 제네릭이 있습니다.

템플릿은 이름에서 알 수 있듯이 템플릿 매개변수를 채워 유형이 안전한 코드를 생성하는 데 사용할 수 있는 (잠깐 기다리세요...) 템플릿을 컴파일러에 제공함으로써 작동합니다.

제가 이해하는 대로 제네릭은 반대 방향으로 작동합니다.유형 매개변수는 이를 사용하는 코드가 유형에 안전한지 확인하기 위해 컴파일러에서 사용되지만 결과 코드는 유형이 전혀 없이 생성됩니다.

C++ 템플릿을 다음과 같이 생각하세요. 정말 좋다 매크로 시스템 및 Java 제네릭을 자동으로 유형 변환을 생성하는 도구로 사용합니다.

 

C++ 템플릿에는 있지만 Java 제네릭에는 없는 또 다른 기능은 전문화입니다.이를 통해 특정 유형에 대해 다른 구현을 가질 수 있습니다.예를 들어, 다음과 같은 분야에 고도로 최적화된 버전을 가질 수 있습니다. 정수, 나머지 유형에 대해서는 여전히 일반 버전이 있습니다.또는 포인터 유형과 포인터가 아닌 유형에 대해 서로 다른 버전을 가질 수 있습니다.이는 포인터를 건네줄 때 역참조된 개체에 대해 작업을 수행하려는 경우 유용합니다.

이 주제에 대한 훌륭한 설명이 있습니다. Java 제네릭 및 컬렉션모리스 나프탈린(Maurice Naftalin), 필립 와들러(Philip Wadler) 지음.나는 이 책을 적극 추천한다.인용하자면:

Java의 제네릭은 C ++의 템플릿과 유사합니다....구문은 의도적으로 비슷하며 의미론은 의도적으로 다릅니다....의미 적으로, Java 제네릭은 C ++ 템플릿으로 확장에 의해 정의되는 Erasure에 의해 정의됩니다.

전체 설명을 읽어주세요 여기.

alt text
(원천: oreilly.com)

기본적으로 AFAIK, C++ 템플릿은 각 유형에 대한 코드 복사본을 생성하는 반면 Java 제네릭은 정확히 동일한 코드를 사용합니다.

그래요 당신 말할 수있다 해당 C++ 템플릿은 Java 일반과 동일합니다. 개념 (Java 제네릭이 개념상 C++와 동일하다고 말하는 것이 더 적절하지만)

C++의 템플릿 메커니즘에 익숙하다면 제네릭이 비슷하다고 생각할 수도 있지만 유사성은 피상적입니다.제네릭은 각 전문화에 대해 새 클래스를 생성하지 않으며 "템플릿 메타프로그래밍"을 허용하지도 않습니다.

에서: 자바 제네릭

Java(및 C#) 제네릭은 간단한 런타임 유형 대체 메커니즘인 것 같습니다.
C++ 템플릿은 필요에 맞게 언어를 수정할 수 있는 방법을 제공하는 컴파일 타임 구성입니다.이는 실제로 컴파일 중에 컴파일러가 실행하는 순수 기능 언어입니다.

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에서는 가능하다고 생각하지 않습니다.

한 문장으로 요약하겠습니다.템플릿은 새로운 유형을 생성하고 제네릭은 기존 유형을 제한합니다.

@키이스:

해당 코드는 실제로 잘못된 것이며 작은 결함(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); }

아래 답변은 책에 나온 내용입니다. 코딩 인터뷰 크래킹 제 생각에 13장에 대한 솔루션은 매우 좋다고 생각합니다.

Java 제네릭의 구현은 '유형 삭제'라는 아이디어에 뿌리를 두고 있습니다. 이 기술은 소스 코드가 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 제네릭의 사용은 실제로 우리의 기능에 큰 변화를 주지 않았습니다.그것은 단지 상황을 좀 더 아름답게 만들었습니다.이러한 이유로 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++ 템플릿에는 다른 여러 가지 차이점이 있습니다.여기에는 다음이 포함됩니다.

  • C++ 템플릿은 int와 같은 기본 유형을 사용할 수 있습니다.Java는 대신 정수를 사용해야합니다.
  • Java에서는 템플릿의 유형 매개 변수를 특정 유형으로 제한 할 수 있습니다.예를 들어, 제네릭을 사용하여 CardDeck을 구현하고 유형 매개 변수가 CardGame에서 확장되어야하도록 지정할 수 있습니다.
  • C ++에서 유형 매개 변수는 인스턴스화 될 수 있지만 Java는이를 지원하지 않습니다.
  • Java에서는 유형 매개 변수 (예 : MyClass의 FOO)를 정적 메소드 및 변수에 사용할 수 없습니다. 이는 MyClass와 MyClass 사이에 공유되므로.C++에서는 이러한 클래스가 다르기 때문에 유형 매개변수를 정적 메서드 및 변수에 사용할 수 있습니다.
  • Java에서는 유형 매개변수에 관계없이 MyClass의 모든 인스턴스가 동일한 유형입니다.유형 매개변수는 런타임 시 지워집니다.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++로 다시 작성하려고 하면 템플릿이 무한 확장된다고 불평하며 컴파일되지 않습니다.

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