C ++ 시절에 C 스타일 캐스트 연산자의 악에 대해 배웠던 나는 처음에는 Java 5 java.lang.Class
에서 cast
메소드를 얻었음 을 알게되어 기뻤습니다 .
드디어 캐스팅에 대한 OO 방식이 있다고 생각했습니다.
결과 Class.cast
는 static_cast
C ++에서 와 동일하지 않습니다 . 더 비슷 reinterpret_cast
합니다. 예상되는 곳에 컴파일 오류가 발생하지 않고 대신 런타임으로 지연됩니다. 다음은 다양한 동작을 보여주는 간단한 테스트 사례입니다.
package test;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class TestCast
{
static final class Foo
{
}
static class Bar
{
}
static final class BarSubclass
extends Bar
{
}
@Test
public void test ( )
{
final Foo foo = new Foo( );
final Bar bar = new Bar( );
final BarSubclass bar_subclass = new BarSubclass( );
{
final Bar bar_ref = bar;
}
{
// Compilation error
final Bar bar_ref = foo;
}
{
// Compilation error
final Bar bar_ref = (Bar) foo;
}
try
{
// !!! Compiles fine, runtime exception
Bar.class.cast( foo );
}
catch ( final ClassCastException ex )
{
assertTrue( true );
}
{
final Bar bar_ref = bar_subclass;
}
try
{
// Compiles fine, runtime exception, equivalent of C++ dynamic_cast
final BarSubclass bar_subclass_ref = (BarSubclass) bar;
}
catch ( final ClassCastException ex )
{
assertTrue( true );
}
}
}
그래서 이것이 제 질문입니다.
Class.cast()
제네릭 땅으로 추방 되어야합니까 ? 거기에는 합법적 인 용도가 꽤 있습니다.- 컴파일러는를
Class.cast()
사용할 때 컴파일 오류를 생성해야 하며 컴파일 타임에 잘못된 조건을 확인할 수 있습니까? - Java는 C ++와 유사한 언어 구조로 캐스트 연산자를 제공해야합니까?
답변
나는 Class.cast(Object)
“제네릭 땅”에서 경고를 피하기 위해서만 사용 했습니다. 나는 종종 다음과 같은 일을하는 메소드를 본다.
@SuppressWarnings("unchecked")
<T> T doSomething() {
Object o;
// snip
return (T) o;
}
다음과 같이 교체하는 것이 가장 좋습니다.
<T> T doSomething(Class<T> cls) {
Object o;
// snip
return cls.cast(o);
}
그것이 Class.cast(Object)
내가 본 유일한 사용 사례입니다 .
컴파일러 경고에 관하여 : 나는 그것이 Class.cast(Object)
컴파일러에게 특별하지 않다고 생각합니다 . 정적으로 사용 (즉 때를 최적화 할 수있는 Foo.class.cast(o)
것이 아니라 cls.cast(o)
)하지만 난 그것을 사용하는 사람을 본 적이 – 다소 쓸모없는 컴파일러에이 최적화를 구축하는 노력을 기울이고있다.
답변
첫째, 거의 모든 캐스트를하지 않도록 강력히 권장하므로 가능한 한 제한해야합니다! 자바의 강력한 컴파일 타임 기능의 이점을 잃게됩니다.
어쨌든 리플렉션을 통해 토큰을 Class.cast()
검색 할 때 주로 사용해야합니다 Class
. 쓰는 것이 더 관용적입니다.
MyObject myObject = (MyObject) object
보다는
MyObject myObject = MyObject.class.cast(object)
편집 : 컴파일시 오류
전반적으로 Java는 런타임에만 캐스트 검사를 수행합니다. 그러나 컴파일러는 그러한 캐스트가 결코 성공할 수 없다는 것을 증명할 수있는 경우 오류를 발행 할 수 있습니다 (예 : 클래스를 상위 유형이 아닌 다른 클래스로 캐스트하고 최종 클래스 유형을 해당 유형 계층 구조에없는 클래스 / 인터페이스로 캐스트). 여기부터 Foo
하고 Bar
서로 계층 구조에없는 클래스이며, 캐스트는 성공할 수 없다.
답변
언어간에 구문과 개념을 번역하고 번역하는 것은 항상 문제가되고 종종 오해의 소지가 있습니다. 캐스팅도 예외는 아닙니다. 특히 Java는 동적 언어이고 C ++는 다소 다릅니다.
Java의 모든 캐스팅은 수행 방법에 관계없이 런타임에 수행됩니다. 유형 정보는 런타임에 보관됩니다. C ++는 좀 더 혼합 된 것입니다. C ++의 구조체를 다른 구조체로 캐스팅 할 수 있으며 이는 해당 구조체를 나타내는 바이트의 재해 석일뿐입니다. Java는 그렇게 작동하지 않습니다.
또한 Java와 C ++의 제네릭은 크게 다릅니다. Java에서 C ++ 작업을 수행하는 방법에 지나치게 신경 쓰지 마십시오. Java 방식으로 작업하는 방법을 배워야합니다.
답변
Class.cast()
Java 코드에서는 거의 사용되지 않습니다. 사용되는 경우 일반적으로 런타임에만 알려진 유형 (즉, 각각의 Class
객체 및 일부 유형 매개 변수를 통해)을 사용합니다. 제네릭을 사용하는 코드에서만 정말 유용합니다 (이게 이전에 소개되지 않은 이유이기도합니다).
그것은 것입니다 하지 유사 reinterpret_cast
가 있기 때문에, 아니 당신이 더 이상 않는 정상 캐스트에 비해 런타임에 유형의 시스템을 망가뜨릴 수 (즉, 당신이 할 수 휴식 제네릭 형식 매개 변수를 할 수는 없지만 휴식 “진짜”유형).
C 스타일 캐스트 연산자의 악은 일반적으로 Java에 적용되지 않습니다. C 스타일 캐스트처럼 보이는 Java 코드는dynamic_cast<>()
의 참조 유형 와 (Java에는 런타임 유형 정보가 있음).
일반적으로 C ++ 캐스팅 연산자를 Java 캐스팅과 비교하는 것은 Java에서는 참조 만 캐스팅 할 수 있고 객체에 대한 변환이 발생하지 않기 때문에 매우 어렵습니다 (이 구문을 사용하여 기본 값만 변환 할 수 있음).
답변
일반적으로 캐스트 연산자는보다 간결하고 컴파일러에서 분석하여 코드에 대한 노골적인 문제를 뱉어 낼 수 있기 때문에 Class # cast 메서드보다 선호됩니다.
Class # cast는 컴파일이 아닌 런타임에 유형 검사를 담당합니다.
Class # cast에 대한 사용 사례가 있습니다. 특히 반사 작업과 관련하여 특히 그렇습니다.
람다가 자바에 왔기 때문에 예를 들어 추상 유형으로 작업하는 경우 컬렉션 / 스트림 API와 함께 Class # cast를 사용하는 것을 개인적으로 좋아합니다.
Dog findMyDog(String name, Breed breed) {
return lostAnimals.stream()
.filter(Dog.class::isInstance)
.map(Dog.class::cast)
.filter(dog -> dog.getName().equalsIgnoreCase(name))
.filter(dog -> dog.getBreed() == breed)
.findFirst()
.orElse(null);
}
답변
C ++와 Java는 서로 다른 언어입니다.
Java C 스타일 캐스트 연산자는 C / C ++ 버전보다 훨씬 더 제한적입니다. 효과적으로 자바 캐스트는 C ++ dynamic_cast와 비슷합니다. 만약 당신이 가지고있는 객체를 새로운 클래스로 캐스트 할 수 없다면 런타임 (또는 코드에 충분한 정보가있는 경우 컴파일 시간) 예외가 발생합니다. 따라서 C 유형 캐스트를 사용하지 않는다는 C ++ 아이디어는 Java에서 좋은 아이디어가 아닙니다.
답변
가장 많이 언급했듯이 추악한 캐스트 경고를 제거하는 것 외에도 Class.cast는 일반적으로 일반 캐스팅과 함께 사용되는 런타임 캐스트입니다. 일반 정보가 런타임에 지워지고 각 일반이 Object로 간주되는 방식으로 인해 초기 ClassCastException을 던집니다.
예를 들어 serviceLoder는 객체를 생성 할 때이 트릭을 사용합니다. S p = service.cast (c.newInstance ()); SP = (S) c.newInstance (); 일 때 클래스 캐스트 예외가 발생합니다. ‘Type safety : Unchecked cast from Object to S’ 경고가 표시되지 않고 표시 될 수 있습니다 . (Object P = (Object) c.newInstance ();)
-단순히 캐스팅 된 개체가 캐스팅 클래스의 인스턴스인지 확인한 다음 캐스팅 연산자를 사용하여 경고를 표시하지 않고 숨 깁니다.
동적 캐스트를위한 자바 구현 :
@SuppressWarnings("unchecked")
public T cast(Object obj) {
if (obj != null && !isInstance(obj))
throw new ClassCastException(cannotCastMsg(obj));
return (T) obj;
}
private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
fail(service,
"Provider " + cn + " not found");
}
if (!service.isAssignableFrom(c)) {
fail(service,
"Provider " + cn + " not a subtype");
}
try {
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
throw new Error(); // This cannot happen
}