Java 클래스에서 표준 이름, 단순 이름 및 클래스 이름의 차이점은 무엇입니까?


972

Java에서는 다음과 같은 차이점이 있습니다.

Object o1 = ....
o1.getClass().getSimpleName();
o1.getClass().getName();
o1.getClass().getCanonicalName();

Javadoc을 여러 번 확인했지만 아직 잘 설명하지 못했습니다. 또한 테스트를 실행했는데 이러한 메소드가 호출되는 방식의 실제 의미를 반영하지 않았습니다.



218
나는 이것이 합리적인 질문이라고 생각합니다. javadoc은이 세 가지의 차이점을 잘 설명하지 못합니다.
Graham Borland

1
docs.oracle.com/javase/6/docs/api/java/lang/Class.html을 참조 하거나 테스트를 작성하십시오.
Nick Holt

7
@GrahamBorland javadoc에 "Java Language Specification에 정의 된대로"가 표시 되므로 해당 문서에서 찾을 수 있습니다. 클릭 가능한 링크가 아니기 때문에 사람들은 최소한의 노력을 기울이고 첫 번째 검색 엔진 결과를 클릭 할 수 있습니다.
vbence

66
@vbence : 대부분의 사람들은 JLS에서 이와 같은 사소한 것을 찾는 것보다 오히려 일을 끝내기를 원합니다. 따라서 이것은 첫 번째 Google 결과입니다.)
pathikrit

답변:


1130

확실하지 않은 경우 먼저 테스트를 작성하십시오.

나는 이걸했다:

class ClassNameTest {
    public static void main(final String... arguments) {
        printNamesForClass(
            int.class,
            "int.class (primitive)");
        printNamesForClass(
            String.class,
            "String.class (ordinary class)");
        printNamesForClass(
            java.util.HashMap.SimpleEntry.class,
            "java.util.HashMap.SimpleEntry.class (nested class)");
        printNamesForClass(
            new java.io.Serializable(){}.getClass(),
            "new java.io.Serializable(){}.getClass() (anonymous inner class)");
    }

    private static void printNamesForClass(final Class<?> clazz, final String label) {
        System.out.println(label + ":");
        System.out.println("    getName():          " + clazz.getName());
        System.out.println("    getCanonicalName(): " + clazz.getCanonicalName());
        System.out.println("    getSimpleName():    " + clazz.getSimpleName());
        System.out.println("    getTypeName():      " + clazz.getTypeName()); // added in Java 8
        System.out.println();
    }
}

인쇄물:

int.class (프리미티브) :
    getName () : int
    getCanonicalName () : int
    getSimpleName () : int
    getTypeName () : int

String.class (일반 클래스) :
    getName () : java.lang.String
    getCanonicalName () : java.lang.String
    getSimpleName () : 문자열
    getTypeName () : java.lang.String

java.util.HashMap.SimpleEntry.class (중첩 된 클래스) :
    getName () : java.util.AbstractMap $ SimpleEntry
    getCanonicalName () : java.util.AbstractMap.SimpleEntry
    getSimpleName () : SimpleEntry
    getTypeName () : java.util.AbstractMap $ SimpleEntry

새로운 java.io.Serializable () {}. getClass () (익명 내부 클래스) :
    getName () : ClassNameTest $ 1
    getCanonicalName () : null
    getSimpleName () :    
    getTypeName () : ClassNameTest $ 1

마지막 블록에는 getSimpleName빈 문자열이 반환되는 빈 항목이 있습니다 .

이 결과는 다음과 같습니다.

  • 이름은 동적으로, 예를 들어, 전화를 가진 클래스를로드하는 데 사용하는 거라고 이름입니다 Class.forName기본으로 ClassLoader. 특정 범위 내에서 ClassLoader모든 클래스는 고유 한 이름을 갖습니다.
  • 정식 이름은 import 문에 사용되는 이름입니다. toString또는 로깅 작업 중에 유용 할 수 있습니다 . 때 javac컴파일러가 클래스 경로의 완전한 뷰를 갖고,이 컴파일시에 정규화 된 클래스 및 패키지 이름이 충돌하여 내 정식 이름의 고유성을 적용합니다. 그러나 JVM은 이러한 이름 충돌을 허용해야하므로 표준 이름은에서 클래스를 고유하게 식별하지 않습니다 ClassLoader. (이 후자의 경우,이 게터의 이름은 더 좋았을 것입니다 getJavaName. 그러나이 방법은 JVM이 Java 프로그램을 실행하는 데만 사용 된 시점부터 시작됩니다.)
  • 간단한 이름은 느슨하게 동안 유용 할 수 있습니다 다시 클래스를 식별 toString또는 기록 작업을하지만, 고유하지 않을 수도 있습니다.
  • 형의 이름 반환, "그것은) toString (같은 : 그것은 순전히 정보의 어떠한 계약 금액이 없습니다" "이러한 유형의 이름에 대한 정보를 문자열"(sir4ur0n에 의해 작성된로)

5
어떤 추가가 필요하다고 생각하십니까?
Nick Holt

2
@AnupamSaini 예. 실제 응용 프로그램에서 이러한 패키지 이름을 갖는 것은 미친 것입니다.
Jayen

3
IT는 미친 짓이지만 악의적 인 행위자가 작동 할 수있는 일종의 가정입니다. "오, 우리는 클래스가 소문자로 시작하지 않을 것입니다. 패키지는 대문자로 시작하지 않을 것입니다." 물론, 클래스 로더에 액세스하는 악의적 인 행위자는 이미 끔찍한 일을 할 수 있으므로 아마도 끔찍한 가정은 아닙니다.
corsiKa

2
@PieterDeBie 어떻게 그렇게? 당신이 알아야 할 것은 테스트하려는 메소드 이름입니다.
fool4jesus

20
Java 8에는 getTypeName ()도 추가되었습니다 ...
시어 도어 머독

90

로컬 클래스, 람다 및 toString()메소드를 추가 하여 이전 두 답변을 완료하십시오. 또한 람다 배열과 익명 클래스 배열을 추가합니다 (실제로는 의미가 없습니다).

package com.example;

public final class TestClassNames {
    private static void showClass(Class<?> c) {
        System.out.println("getName():          " + c.getName());
        System.out.println("getCanonicalName(): " + c.getCanonicalName());
        System.out.println("getSimpleName():    " + c.getSimpleName());
        System.out.println("toString():         " + c.toString());
        System.out.println();
    }

    private static void x(Runnable r) {
        showClass(r.getClass());
        showClass(java.lang.reflect.Array.newInstance(r.getClass(), 1).getClass()); // Obtains an array class of a lambda base type.
    }

    public static class NestedClass {}

    public class InnerClass {}

    public static void main(String[] args) {
        class LocalClass {}
        showClass(void.class);
        showClass(int.class);
        showClass(String.class);
        showClass(Runnable.class);
        showClass(SomeEnum.class);
        showClass(SomeAnnotation.class);
        showClass(int[].class);
        showClass(String[].class);
        showClass(NestedClass.class);
        showClass(InnerClass.class);
        showClass(LocalClass.class);
        showClass(LocalClass[].class);
        Object anonymous = new java.io.Serializable() {};
        showClass(anonymous.getClass());
        showClass(java.lang.reflect.Array.newInstance(anonymous.getClass(), 1).getClass()); // Obtains an array class of an anonymous base type.
        x(() -> {});
    }
}

enum SomeEnum {
   BLUE, YELLOW, RED;
}

@interface SomeAnnotation {}

이것은 전체 출력입니다.

getName():          void
getCanonicalName(): void
getSimpleName():    void
toString():         void

getName():          int
getCanonicalName(): int
getSimpleName():    int
toString():         int

getName():          java.lang.String
getCanonicalName(): java.lang.String
getSimpleName():    String
toString():         class java.lang.String

getName():          java.lang.Runnable
getCanonicalName(): java.lang.Runnable
getSimpleName():    Runnable
toString():         interface java.lang.Runnable

getName():          com.example.SomeEnum
getCanonicalName(): com.example.SomeEnum
getSimpleName():    SomeEnum
toString():         class com.example.SomeEnum

getName():          com.example.SomeAnnotation
getCanonicalName(): com.example.SomeAnnotation
getSimpleName():    SomeAnnotation
toString():         interface com.example.SomeAnnotation

getName():          [I
getCanonicalName(): int[]
getSimpleName():    int[]
toString():         class [I

getName():          [Ljava.lang.String;
getCanonicalName(): java.lang.String[]
getSimpleName():    String[]
toString():         class [Ljava.lang.String;

getName():          com.example.TestClassNames$NestedClass
getCanonicalName(): com.example.TestClassNames.NestedClass
getSimpleName():    NestedClass
toString():         class com.example.TestClassNames$NestedClass

getName():          com.example.TestClassNames$InnerClass
getCanonicalName(): com.example.TestClassNames.InnerClass
getSimpleName():    InnerClass
toString():         class com.example.TestClassNames$InnerClass

getName():          com.example.TestClassNames$1LocalClass
getCanonicalName(): null
getSimpleName():    LocalClass
toString():         class com.example.TestClassNames$1LocalClass

getName():          [Lcom.example.TestClassNames$1LocalClass;
getCanonicalName(): null
getSimpleName():    LocalClass[]
toString():         class [Lcom.example.TestClassNames$1LocalClass;

getName():          com.example.TestClassNames$1
getCanonicalName(): null
getSimpleName():    
toString():         class com.example.TestClassNames$1

getName():          [Lcom.example.TestClassNames$1;
getCanonicalName(): null
getSimpleName():    []
toString():         class [Lcom.example.TestClassNames$1;

getName():          com.example.TestClassNames$$Lambda$1/1175962212
getCanonicalName(): com.example.TestClassNames$$Lambda$1/1175962212
getSimpleName():    TestClassNames$$Lambda$1/1175962212
toString():         class com.example.TestClassNames$$Lambda$1/1175962212

getName():          [Lcom.example.TestClassNames$$Lambda$1;
getCanonicalName(): com.example.TestClassNames$$Lambda$1/1175962212[]
getSimpleName():    TestClassNames$$Lambda$1/1175962212[]
toString():         class [Lcom.example.TestClassNames$$Lambda$1;

여기 규칙이 있습니다. 먼저 기본 유형으로 시작하고 void다음을 수행하십시오.

  1. 클래스 객체가 기본 유형 또는를 나타내는 경우 void네 가지 메소드 모두 이름을 반환합니다.

이제 getName()메소드 의 규칙 :

  1. 모든 람다 및 비 배열 클래스 또는 인터페이스 (예 : 최상위, 중첩, 내부, 로컬 및 익명)는 getName()패키지 이름 뒤에 점 (패키지가있는 경우) 인 이름 (에 의해 반환 됨 )을 갖습니다. ) 다음에 컴파일러에서 생성 한 클래스 파일 이름 (접미사없이)이 붙습니다 .class. 패키지가 없으면 단순히 클래스 파일의 이름입니다. 클래스가 내부, 중첩, 로컬 또는 익명 클래스 인 경우 컴파일러는 $클래스 파일 이름으로 하나 이상을 생성해야합니다 . 익명 클래스의 경우 클래스 이름은 달러 기호와 숫자로 끝납니다.
  2. Lambda 클래스 이름은 일반적으로 예측할 수 없으므로 어쨌든 신경 쓰지 않아야합니다. 정확하게 그들의 이름은 다음에 오는 클래스의 이름입니다.$$Lambda$ , 뒤에, 숫자, 슬래시, 다른 숫자가옵니다.
  3. 프리미티브의 클래스 기술자는 Z위해 boolean, B위해 byte, S위해 short, C위해 char, I위해 int, J위해 long, F위해 floatD위해 double. 비 배열 클래스 및 인터페이스의 경우 클래스 디스크립터 L다음에 getName()뒤에 오는 것이 표시됩니다.; . 배열 클래스의 경우 클래스 설명자 [뒤에 구성 요소 유형의 클래스 설명자 (자체 배열 클래스 일 수 있음)가옵니다.
  4. 배열 클래스의 경우 getName() 메소드는 클래스 디스크립터를 리턴합니다. 이 규칙은 구성 요소 유형이 람다 (버그 일 가능성이있는) 인 배열 클래스에만 실패하는 것으로 보이지만, 구성 요소 유형이 람다 인 배열 클래스가 존재하더라도 아무 의미가 없기 때문에 이것은 중요하지 않습니다.

이제 toString()방법 :

  1. 클래스 인스턴스가 인터페이스 (또는 특수한 유형의 인터페이스 인 주석)를 나타내는 경우를 toString()반환합니다 "interface " + getName(). 프리미티브 인 경우 간단히 반환합니다 getName(). 그것이 다른 것이라면 (클래스 타입이라면 꽤 이상한 것이더라도) 반환합니다 "class " + getName().

그만큼 getCanonicalName()방법 :

  1. 최상위 클래스 및 인터페이스의 경우 getCanonicalName() 메소드는 메소드가 리턴하는 내용 만 getName()리턴합니다.
  2. 그만큼 getCanonicalName() 메소드는 null익명 또는 로컬 클래스 및 해당 클래스의 배열 클래스에 대해 리턴 합니다.
  3. 내부 및 중첩 클래스 및 인터페이스의 경우이 getCanonicalName()메소드는getName() 컴파일러가 도입 한 달러 기호를 점으로 대체하는 .
  4. 배열 클래스의 경우, 구성 요소 유형의 표준 이름이 인 경우 getCanonicalName()메소드가 리턴 null합니다 null. 그렇지 않으면, 구성 요소 유형의 정식 이름 뒤에을 리턴합니다 [].

getSimpleName()방법 :

  1. 최상위, 중첩, 내부 및 로컬 클래스의 경우 getSimpleName() 경우 소스 파일에 작성된 클래스의 이름을 반환합니다.
  2. 익명 클래스의 getSimpleName()경우 빈을 반환합니다.String .
  3. 람다 클래스의 경우 패키지 이름없이 반환 getSimpleName()되는 getName()것을 반환합니다. 이것은별로 이해가되지 않으며 나에게 버그처럼 보이지만 전화하는 데는 아무런 의미가 없습니다.getSimpleName() 람다 클래스를 필요 .
  4. 배열 클래스의 getSimpleName()경우이 메소드는 구성 요소 클래스의 단순 이름과 그 뒤에 오는 이름을 리턴합니다 []. 이것은 구성 요소 유형이 익명 클래스 인 배열 클래스가 []단순한 이름과 같은 재미있는 부작용을 가지고 있습니다.

2
… replacing the dollar-signs by dots: 구분 기호로 도입 된 달러 기호 만 교체됩니다. 간단한 이름의 일부로 달러를 가질 수 있으며 그 자리에 남아 있습니다.
MvG

아뇨! 수업 이름의 일부로! 나는 클래스 변압기를 개발 그리고 난 '/'클래스와 패키지 이름 사이에 안전 구분이 될 것이라고 생각 : /
호세 로베르토 아라 우호 주니어

81

Nick Holt의 관찰 외에도 Array데이터 유형에 대해 몇 가지 사례를 실행했습니다 .

//primitive Array
int demo[] = new int[5];
Class<? extends int[]> clzz = demo.getClass();
System.out.println(clzz.getName());
System.out.println(clzz.getCanonicalName());
System.out.println(clzz.getSimpleName());       

System.out.println();


//Object Array
Integer demo[] = new Integer[5]; 
Class<? extends Integer[]> clzz = demo.getClass();
System.out.println(clzz.getName());
System.out.println(clzz.getCanonicalName());
System.out.println(clzz.getSimpleName());

위의 코드 스 니펫 인쇄 :

[I
int[]
int[]

[Ljava.lang.Integer;
java.lang.Integer[]
Integer[]

28
위의 답변에 대한 수정안을 제안하는 것이 낫지 않습니다.
LoKi

17

나는 다양한 이름 지정 체계에 혼란을 겪었으며 여기 에서이 질문을 찾았을 때 내 자신의 질문에 대답하고 싶었습니다. 내 연구 결과가 충분히 적합하다고 생각하고 이미 존재하는 것을 보완합니다. 초점은 문서를 찾고 있습니다 다양한 용어에 를 다른 곳에서 발생할 수있는 관련 용어를 추가하는 것입니다.

다음 예제를 고려하십시오.

package a.b;
class C {
  static class D extends C {
  }
  D d;
  D[] ds;
}
  • 간단한 이름D입니다 D. 그것은 수업을 선언 할 때 쓴 부분입니다. 익명 클래스 에는 간단한 이름이 없습니다. Class.getSimpleName()이 이름 또는 빈 문자열을 반환합니다. JLS 섹션 3.8에 따라 식별자의 유효한 부분 $이기 때문에 간단한 이름에 이와 같이 쓰면 if 가 포함될 수 있습니다 (추천하지 않더라도).$

  • 에 따르면 JLS 섹션 6.7 , 모두 a.b.C.D와는 a.b.C.D.D.D정규화 된 이름 ,하지만 a.b.C.D정식 이름D. 따라서 모든 정식 이름은 정규화 된 이름이지만 대화가 항상 사실은 아닙니다. Class.getCanonicalName()정식 이름 또는을 반환합니다 null.

  • Class.getName()JLS 섹션 13.1에 지정된대로 이진 이름 을 반환하도록 문서화되어 있습니다. 이 경우에는 반환 을 위해 및 위해 .a.b.C$DD[La.b.C$D;D[]

  • 이 대답 은 동일한 클래스 로더에 의해로드 된 두 클래스가 동일한 표준 이름 이지만 별개의 이진 이름 을 가질 수 있음을 보여줍니다 . 어떤 이름도 다른 이름을 확실하게 추론하기에는 충분하지 않습니다. 정식 이름이 있으면 이름의 어느 부분이 패키지이고 어떤 부분이 클래스를 포함하는지 알 수 없습니다. 이진 이름이 있으면 $구분자로 소개 된 것과 간단한 이름의 일부를 알 수 없습니다 . (클래스 파일 을 저장 바이너리 이름클래스 자체 와 그 둘러싸는 클래스 런타임이 할 수 있습니다, 이 구별을 .)

  • 익명 클래스로컬 클래스 에는 정규화 된 이름이 없지만 이진 이름이 있습니다. 이러한 클래스 안에 중첩 된 클래스도 마찬가지입니다. 모든 클래스에는 이진 이름이 있습니다.

  • 실행 javap -v -privatea/b/C.class프로그램은 바이트 코드의 유형을 의미하는 것이 d아니라 La/b/C$D;어레이의 ds[La/b/C$D;. 이를 디스크립터 라고 하며 JVMS 섹션 4.3에 지정되어 있습니다.

  • a/b/C$D두 가지 설명자 모두에 사용되는 클래스 이름 은 이진 이름 으로 대체 .하여 얻는 것 /입니다. JVM 스펙은 분명히 이것을 이진 이름내부 형식 이라고 부릅니다 . JVMS 섹션 4.2.1에서는이를 설명하고 이진 이름과의 차이는 역사적인 이유로 인한 것임을 설명합니다.

  • 일반적인 파일 이름 기반 클래스 로더 중 하나에있는 클래스 의 파일 이름/이진 이름의 내부 형식으로 디렉토리 구분 기호로 해석 하고 파일 이름 확장자 .class를 추가하면 얻을 수있는 것입니다. 문제의 클래스 로더가 사용하는 클래스 경로를 기준으로 해결되었습니다.


3
이것은 JLS를 참조하고 적절한 용어를 사용하는 유일한 대답이므로 허용되는 답변이어야합니다.
John

10

이것은 getName (), getSimpleName (), getCanonicalName ()을 설명하는 가장 좋은 문서입니다.

https://javahowtodoit.wordpress.com/2014/09/09/java-lang-class-what-is-the-difference-between-class-getname-class-getcanonicalname-and-class-getsimplename/

// Primitive type
int.class.getName();          // -> int
int.class.getCanonicalName(); // -> int
int.class.getSimpleName();    // -> int

// Standard class
Integer.class.getName();          // -> java.lang.Integer
Integer.class.getCanonicalName(); // -> java.lang.Integer
Integer.class.getSimpleName();    // -> Integer

// Inner class
Map.Entry.class.getName();          // -> java.util.Map$Entry
Map.Entry.class.getCanonicalName(); // -> java.util.Map.Entry
Map.Entry.class.getSimpleName();    // -> Entry     

// Anonymous inner class
Class<?> anonymousInnerClass = new Cloneable() {}.getClass();
anonymousInnerClass.getName();          // -> somepackage.SomeClass$1
anonymousInnerClass.getCanonicalName(); // -> null
anonymousInnerClass.getSimpleName();    // -> // An empty string

// Array of primitives
Class<?> primitiveArrayClass = new int[0].getClass();
primitiveArrayClass.getName();          // -> [I
primitiveArrayClass.getCanonicalName(); // -> int[]
primitiveArrayClass.getSimpleName();    // -> int[]

// Array of objects
Class<?> objectArrayClass = new Integer[0].getClass();
objectArrayClass.getName();          // -> [Ljava.lang.Integer;
objectArrayClass.getCanonicalName(); // -> java.lang.Integer[]
objectArrayClass.getSimpleName();    // -> Integer[]

3

것을주의하는 것이 재미있다 getCanonicalName()getSimpleName()올릴 수 있습니다InternalError 클래스 이름이 올바르지 않은 경우에. 이는 Java 이외의 일부 JVM 언어 (예 : Scala)에서 발생합니다.

다음을 고려하십시오 (Java 8의 경우 Scala 2.11).

scala> case class C()
defined class C

scala> val c = C()
c: C = C()

scala> c.getClass.getSimpleName
java.lang.InternalError: Malformed class name
  at java.lang.Class.getSimpleName(Class.java:1330)
  ... 32 elided

scala> c.getClass.getCanonicalName
java.lang.InternalError: Malformed class name
  at java.lang.Class.getSimpleName(Class.java:1330)
  at java.lang.Class.getCanonicalName(Class.java:1399)
  ... 32 elided

scala> c.getClass.getName
res2: String = C

이는 혼합 언어 환경이나 바이트 코드를 동적으로로드하는 환경 (예 : 앱 서버 및 기타 플랫폼 소프트웨어)에 문제가 될 수 있습니다.


1
    public void printReflectionClassNames(){
    StringBuffer buffer = new StringBuffer();
    Class clazz= buffer.getClass();
    System.out.println("Reflection on String Buffer Class");
    System.out.println("Name: "+clazz.getName());
    System.out.println("Simple Name: "+clazz.getSimpleName());
    System.out.println("Canonical Name: "+clazz.getCanonicalName());
    System.out.println("Type Name: "+clazz.getTypeName());
}

outputs:
Reflection on String Buffer Class
Name: java.lang.StringBuffer
Simple Name: StringBuffer
Canonical Name: java.lang.StringBuffer
Type Name: java.lang.StringBuffer

1
내부에있어서 처음 두 줄은 감소 될 수있다Class<StringBuffer> clazz = StringBuffer.class
ThePyroEagle

1

getName () –이 Class 객체가 나타내는 엔티티 (클래스, 인터페이스, 배열 클래스, 기본 유형 또는 void)의 이름을 문자열로 반환합니다.

getCanonicalName () – Java 언어 사양에 정의 된 기본 클래스의 정식 이름을 반환합니다.

getSimpleName () – 기본 클래스의 단순 이름, 즉 소스 코드에 지정된 이름을 반환합니다.

package com.practice;

public class ClassName {
public static void main(String[] args) {

  ClassName c = new ClassName();
  Class cls = c.getClass();

  // returns the canonical name of the underlying class if it exists
  System.out.println("Class = " + cls.getCanonicalName());    //Class = com.practice.ClassName
  System.out.println("Class = " + cls.getName());             //Class = com.practice.ClassName
  System.out.println("Class = " + cls.getSimpleName());       //Class = ClassName
  System.out.println("Class = " + Map.Entry.class.getName());             // -> Class = java.util.Map$Entry
  System.out.println("Class = " + Map.Entry.class.getCanonicalName());    // -> Class = java.util.Map.Entry
  System.out.println("Class = " + Map.Entry.class.getSimpleName());       // -> Class = Entry 
  }
}

한 가지 차이점은 익명 클래스 를 사용하는 경우getCanonicalName()

또 다른 사실은 메소드가 내부 클래스getName()getCanonicalName()메소드와 다르게 작동 한다는 것입니다 .getName()엔 클로징 클래스 표준 이름과 내부 클래스 단순 이름 사이의 구분 기호로 달러를 사용합니다.

에 대해 자세히 알 수있는 자바 클래스 이름을 검색하는 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.