java : 한 유형에서 다른 유형으로 변수를 동적으로 캐스팅하려면 어떻게해야합니까?


85

Java 변수에 대한 동적 캐스팅을 수행하고 싶습니다. 캐스팅 유형은 다른 변수에 저장됩니다.

이것은 일반 캐스팅입니다.

 String a = (String) 5;

이것이 내가 원하는거야:

 String theType = 'String';
 String a = (theType) 5;

이것이 가능합니까? 그렇다면 어떻게해야합니까? 감사!

최신 정보

HashMap받은 클래스를 채우려 고합니다 .

이것은 생성자입니다.

public ConnectParams(HashMap<String,Object> obj) {

    for (Map.Entry<String, Object> entry : obj.entrySet()) {
        try {
            Field f =  this.getClass().getField(entry.getKey());                
            f.set(this, entry.getValue()); /* <= CASTING PROBLEM */
        } catch (NoSuchFieldException ex) {
            log.error("did not find field '" + entry.getKey() + '"');
        } catch (IllegalAccessException ex) {
            log.error(ex.getMessage());         
        }
    }

}

여기서 문제는 클래스의 일부 변수가 유형 Double이고 숫자 3이 수신되면 Integer유형 문제가 있다는 것입니다.


그건 말이 안 돼. 변수 이름이 문자열을 문자열로 캐스팅하는 유형이 되길 원하십니까? 뭐?
cletus

3
나는 대답을 모르지만 이것이 유지 관리 지옥이 될 수 있다는 것이 두렵습니다. Java를 직접 배우는 것이지만 이와 같은 접근이 필요한 상황은 피할 것입니다. 나는 당신이하는 모든 것이 더 나은 방식으로 구현 될 수 있다고 확신합니다. 단지 2 센트입니다.
Sejanus

좋아, 내가 달성하려는 것에 대해 더 많은 정보를 제공 할 것입니다.
ufk

또한 아래 내 답변을 업데이트했습니다!
user85421 2010 년

답변:


14

업데이트와 관련하여 Java에서이 문제를 해결하는 유일한 방법은 많은 ifelseinstanceof표현식으로 모든 경우를 다루는 코드를 작성하는 것입니다 . 당신이 시도하는 것은 마치 동적 언어로 프로그래밍하는 데 익숙한 것처럼 보입니다. 정적 언어에서는 시도하는 것이 거의 불가능하며 시도하려는 작업에 대해 완전히 다른 접근 방식을 선택할 것입니다. 정적 언어는 동적 언어만큼 유연하지 않습니다. :)

Java 모범 사례의 좋은 예는 BalusC답변 (예 :)ObjectConverterAndreas_D답변 (예 : Adapter아래)입니다.


말이 안 돼요

String a = (theType) 5;

의 유형 a은 정적으로 바인딩되어 있으므로이 정적 유형에 String대한 동적 캐스트를 갖는 것은 의미가 없습니다.

추신 : 예제의 첫 번째 줄은 다음과 같이 작성할 수 Class<String> stringClass = String.class;있지만 여전히 stringClass변수를 캐스팅 하는 데 사용할 수 없습니다 .


내가 게시 한 업데이트가 내가하려는 작업을 설명하기를 바랍니다. 나는 PHP 배경에서 왔기 때문에 자바에서는 이것이 가능하지 않을 수도 있습니다.
ufk

정확히, Java에서는 그렇게 동적이 될 수 없습니다. 내 업데이트도 참조하십시오.
akuhn

아래 BalusC의 대답을 참조하십시오. 이것은 당신이 가야 할 길이 (그리고 고통)입니다 ...
akuhn

나는 이것이 늦었다는 것을 알고 있지만 그는 [thetype] a = (thetype) some_object; 무의미하다 당신은 단지 = some_object 벌금 오브젝트 할 수 있기 때문에
조지 자비

120

Reflection을 사용하여 가능합니다.

Object something = "something";
String theType = "java.lang.String";
Class<?> theClass = Class.forName(theType);
Object obj = theClass.cast(something);

그러나 결과 객체는 Object유형 의 변수에 저장되어야하므로 그다지 의미가 없습니다 . 주어진 클래스의 변수가 필요한 경우 해당 클래스로 캐스트 할 수 있습니다.

Number예를 들어 , 주어진 클래스를 얻으려면 :

Object something = new Integer(123);
String theType = "java.lang.Number";
Class<? extends Number> theClass = Class.forName(theType).asSubclass(Number.class);
Number obj = theClass.cast(something);

하지만 여전히 그렇게 할 필요가 없습니다 Number..

개체를 캐스팅해도 아무것도 변경되지 않습니다. 그것은 단지 인 방법 컴파일러 취급 그것.
이와 같은 작업을 수행하는 유일한 이유는 객체가 주어진 클래스 또는 그 하위 클래스의 인스턴스인지 확인하는 것입니다. 그러나 instanceof또는 사용하는 것이 더 좋습니다 Class.isInstance().

최신 정보

마지막에 따라 업데이트가 진짜 문제는 당신이이 때문이다 Integer에서 HashMapA를 할당해야합니다 Double. 이 경우 할 수있는 일은 필드의 유형을 확인하고 다음 xxxValue()방법을 사용하는 것 입니다.Number

...
Field f =  this.getClass().getField(entry.getKey());
Object value = entry.getValue();
if (Integer.class.isAssignableFrom(f.getType())) {
    value = Integer.valueOf(((Number) entry.getValue()).intValue());
} else if (Double.class.isAssignableFrom(f.getType())) {
    value = Double.valueOf(((Number) entry.getValue()).doubleValue());
} // other cases as needed (Long, Float, ...)
f.set(this, value);
...

(내가에서 잘못된 유형을 갖는 아이디어를 좋아하는지 확실하지 않음 Map)


22

이를 위해 일종의 작성이 필요합니다 ObjectConverter. 변환하려는 객체가 있고 변환 할 대상 클래스를 알고있는 경우이 작업을 수행 할 수 있습니다. 이 특별한 경우에는 Field#getDeclaringClass().

당신은 찾을 수 있습니다 여기에 이러한 예를 ObjectConverter. 기본 아이디어를 제공해야합니다. 더 많은 변환 가능성을 원한다면 원하는 인수와 반환 유형을 사용하여 더 많은 메서드를 추가하십시오.


@BalusC-ObjectConverter 코드가 흥미 롭습니다. 사용 사례를 설명해 주시겠습니까?
srini.venigalla 2010 년

구성보다 규칙이 선호되고 소스 유형이 대상 유형과 일치하지 않는 경우에 유용합니다. 2 ~ 3 년 전에 ORM 및 MVC 프레임 워크에서 사용했습니다. 블로그 기사의 선두 텍스트도 참조하십시오.
BalusC 2010 년

12

Class.cast()제공된 매개 변수를 보유한 클래스 인스턴스의 유형으로 동적으로 캐스트 하는 메소드를 사용하여이를 수행 할 수 있습니다. 특정 필드의 클래스 인스턴스를 얻으려면 getType()해당 필드에서 메서드 를 사용합니다 . 아래에 예제를 주었지만 모든 오류 처리를 생략하고 수정하지 않고 사용해서는 안됩니다.

public class Test {

    public String var1;
    public Integer var2;
}

public class Main {

    public static void main(String[] args) throws Exception {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("var1", "test");
        map.put("var2", 1);

        Test t = new Test();

        for (Map.Entry<String, Object> entry : map.entrySet()) {
            Field f = Test.class.getField(entry.getKey());

            f.set(t, f.getType().cast(entry.getValue()));
        }

        System.out.println(t.var1);
        System.out.println(t.var2);
    }
}

1
항목 유형이 필드 유형의 상위 유형이 아닌 경우 어떻게합니까? 그런 다음 프로그래밍 방식으로 변환해야합니다.
BalusC 2010 년

7

아래와 같이 간단한 castMethod를 작성할 수 있습니다.

private <T> T castObject(Class<T> clazz, Object object) {
  return (T) object;
}

귀하의 방법에서 다음과 같이 사용해야합니다.

public ConnectParams(HashMap<String,Object> object) {

for (Map.Entry<String, Object> entry : object.entrySet()) {
    try {
        Field f =  this.getClass().getField(entry.getKey());                
        f.set(this, castObject(entry.getValue().getClass(), entry.getValue()); /* <= CASTING PROBLEM */
    } catch (NoSuchFieldException ex) {
        log.error("did not find field '" + entry.getKey() + '"');
    } catch (IllegalAccessException ex) {    
        log.error(ex.getMessage());          
    }    
}

}

예,이 답변은 질문자가 원하는 것입니다. 그 / 그녀는 Class <?>를 얻고 인스턴스를 "?"클래스로 변환하려고합니다. 기본적으로 java는 이것을 지원하지 않습니다. 하지만 <T>를 사용하면 가능합니다.
ruiruige1991

@ ruiruige1991 이것은 잘못되었습니다. 이 경우 T는 제네릭입니다. Generics는 런타임 동안 아무 작업도 수행하지 않습니다. (T) blah는 유형 삭제로 인해 런타임 중에 (Object) blah가됩니다. 요컨대, 제네릭-> 컴파일 타임이며 런타임에는 영향을 미치지 않습니다. 동적-> 런타임 및 제네릭-> 컴파일 시간 때문에 제네릭은 쓸모가 없습니다.
George Xavier

5

작동하며 접근 방식에 대한 일반적인 패턴 인 어댑터 패턴도 있습니다. 그러나 물론 (1) 자바 프리미티브를 객체로 캐스팅하는 데는 작동하지 않으며 (2) 클래스는 적응 가능해야합니다 (보통 사용자 정의 인터페이스를 구현하여).

이 패턴으로 다음과 같이 할 수 있습니다.

Wolf bigBadWolf = new Wolf();
Sheep sheep = (Sheep) bigBadWolf.getAdapter(Sheep.class);

Wolf 클래스의 getAdapter 메소드 :

public Object getAdapter(Class clazz) {
  if (clazz.equals(Sheep.class)) {
    // return a Sheep implementation
    return getWolfDressedAsSheep(this);
  }

  if (clazz.equals(String.class)) {
    // return a String
    return this.getName();
  }

  return null; // not adaptable
}

당신에게 특별한 아이디어는 불가능합니다. 캐스팅에 문자열 값을 사용할 수 없습니다.


2

당신의 문제는 "동적 캐스팅"의 부족이 아닙니다. 주조 Integer로하는 것은 Double전혀 불가능하다. Java에 한 유형의 객체, 호환되지 않을 수있는 유형의 필드를 제공하고 유형간에 변환하는 방법을 자동으로 파악하도록하는 것 같습니다.

이런 종류의 것은 Java 및 IMO와 같은 강력한 유형의 언어에 대한 혐오입니다.

실제로 무엇을하려고합니까? 반사의 모든 사용은 꽤 비린내처럼 보입니다.


@name : 당신이 계속 제안하는 편집에 관하여 : 내가 기본 값이 아니라 래퍼 클래스 (대문자와 코드로 스타일링으로 표시됨)에 대해 이야기하고 있으며, 그것들 사이의 캐스팅은 확실히 불가능합니다.
Michael Borgwardt 2013

1

이러지마 대신 적절하게 매개 변수화 된 생성자를 사용하십시오. 연결 매개 변수의 집합과 유형은 어쨌든 고정되어 있으므로이 모든 작업을 동적으로 수행 할 필요가 없습니다.


1

그만한 가치가있는 대부분의 스크립팅 언어 (예 : Perl) 및 비 정적 컴파일 시간 언어 (예 : Pick)는 (상대적으로 임의의) 객체 변환에 대한 자동 런타임 동적 문자열을 지원합니다. 이것은 유형 안전성을 잃지 않고 Java에서도 수행 할 수 있으며 정적으로 유형화 된 언어는 동적 캐스팅으로 악한 일을하는 다른 언어의 불쾌한 부작용없이 제공합니다. 의심스러운 수학을 수행하는 Perl 예제 :

print ++($foo = '99');  # prints '100'
print ++($foo = 'a0');  # prints 'a1'

Java에서는 "cross-casting"이라고 부르는 방법을 사용하여 더 잘 수행됩니다 (IMHO). 크로스 캐스팅을 통해 리플렉션은 다음 정적 메서드를 통해 동적으로 검색되는 생성자 및 메서드의 지연로드 캐시에서 사용됩니다.

Object fromString (String value, Class targetClass)

불행히도 Class.cast ()와 같은 내장 Java 메소드는 String에서 BigDecimal로 또는 String에서 Integer로 또는 지원하는 클래스 계층 구조가없는 다른 변환에 대해이를 수행하지 않습니다. 필자의 경우 요점은이를 달성하기위한 완전히 동적 인 방법을 제공하는 것입니다. 이전 참조가 올바른 접근 방식이 아니라고 생각하므로 모든 변환을 코딩해야합니다. 간단히 말해서, 구현은 합법적이거나 가능한 경우 문자열에서 캐스트하는 것입니다.

따라서 해결책은 다음 중 하나의 공개 구성원을 찾는 간단한 반영입니다.

STRING_CLASS_ARRAY = (새 클래스 [] {String.class});

a) 멤버 멤버 = targetClass.getMethod (method.getName (), STRING_CLASS_ARRAY); b) 멤버 멤버 = targetClass.getConstructor (STRING_CLASS_ARRAY);

모든 프리미티브 (Integer, Long 등)와 모든 기본 (BigInteger, BigDecimal 등) 및 심지어 java.regex.Pattern까지 모두이 접근 방식을 통해 다룹니다. 나는 더 엄격한 검사가 필요한 엄청난 양의 임의의 문자열 값 입력이있는 프로덕션 프로젝트에서 상당한 성공을 거두었습니다. 이 접근 방식에서는 메서드가 없거나 메서드가 호출 될 때 예외가 throw됩니다 (BigDecimal에 대한 숫자가 아닌 입력 또는 패턴에 대한 잘못된 RegEx와 같은 잘못된 값이기 때문). 대상 클래스 고유 논리.

이에 대한 몇 가지 단점이 있습니다.

1) 성찰을 잘 이해해야합니다 (초보자가 아니라 조금 복잡합니다). 2) 일부 Java 클래스와 실제로 타사 라이브러리는 (놀랍게도) 제대로 코딩되지 않았습니다. 즉, 단일 문자열 인수를 입력으로 사용하고 대상 클래스의 인스턴스를 반환하는 메서드가 있지만 생각하는 것과 다릅니다 ... Integer 클래스를 고려하십시오.

static Integer getInteger(String nm)
      Determines the integer value of the system property with the specified name.

위의 방법은 실제로 기본 int를 래핑하는 객체로서 Integer와 관련이 없습니다. Reflection은 이것을 디코딩, 값 및 생성자 멤버와 비교하여 문자열에서 정수를 잘못 생성 할 수있는 가능한 후보로 간주합니다. 이는 모두 입력 데이터를 제어 할 수 없지만 원하는 경우 대부분의 임의 문자열 변환에 적합합니다. Integer가 가능한지 알고 있습니다.

위의 문제를 해결하려면 예외를 발생시키는 메서드를 찾는 것이 좋습니다. 이러한 개체의 인스턴스를 만드는 잘못된 입력 값 은 예외를 발생 시켜야 하기 때문 입니다. 불행히도 구현은 예외가 확인 된 것으로 선언되었는지 여부에 따라 다릅니다. 예를 들어 Integer.valueOf (String)는 확인 된 NumberFormatException을 throw하지만 리플렉션 조회 중에 Pattern.compile () 예외를 찾을 수 없습니다. 다시 말하지만,이 동적 "크로스 캐스팅"접근 방식의 실패가 아니라 객체 생성 메서드에서 예외 선언을위한 매우 비표준 구현이라고 생각합니다.

위의 구현 방법에 대한 자세한 내용을 원하는 사람이 있으면 알려주십시오. 그러나이 솔루션은 유형 안전성의 좋은 부분을 잃지 않고 코드가 적고 훨씬 유연하고 확장 가능하다고 생각합니다. 물론 "귀하의 데이터를 아는"것이 항상 최선이지만, 많은 사람들이 알고 있듯이 우리는 관리되지 않는 콘텐츠의 수신자 일 뿐이며 적절하게 사용하기 위해 최선을 다해야합니다.

건배.


1

그래서 이것은 오래된 게시물이지만 내가 그것에 기여할 수 있다고 생각합니다.

항상 다음과 같이 할 수 있습니다.

package com.dyna.test;  

import java.io.File;  
import java.lang.reflect.Constructor;  

public class DynamicClass{  

  @SuppressWarnings("unchecked")  
  public Object castDynamicClass(String className, String value){  
    Class<?> dynamicClass;  

    try  
    {  
      //We get the actual .class object associated with the specified name  
      dynamicClass = Class.forName(className);  



    /* We get the constructor that received only 
     a String as a parameter, since the value to be used is a String, but we could
easily change this to be "dynamic" as well, getting the Constructor signature from
the same datasource we get the values from */ 


      Constructor<?> cons =  
        (Constructor<?>) dynamicClass.getConstructor(new Class<?>[]{String.class});  

      /*We generate our object, without knowing until runtime 
 what type it will be, and we place it in an Object as 
 any Java object extends the Object class) */  
      Object object = (Object) cons.newInstance(new Object[]{value});  

      return object;  
    }  
    catch (Exception e)  
    {  
      e.printStackTrace();  
    }  
    return null;  
  }  

  public static void main(String[] args)  
  {   
    DynamicClass dynaClass = new DynamicClass();  

    /* 
 We specify the type of class that should be used to represent 
 the value "3.0", in this case a Double. Both these parameters 
 you can get from a file, or a network stream for example. */  
    System.out.println(dynaClass.castDynamicClass("java.lang.Double", "3.0"));  

    /* 
We specify a different value and type, and it will work as 
 expected, printing 3.0 in the above case and the test path in the one below, as the Double.toString() and 
 File.toString() would do. */  
    System.out.println(dynaClass.castDynamicClass("java.io.File", "C:\\testpath"));  
  }  

물론 이것은 다른 언어 (예 : Python) 에서처럼 실제로 동적 캐스팅이 아닙니다. java는 정적으로 형식화 된 lang이기 때문입니다. 그러나 이렇게하면 일부 식별자에 따라 다른 방식으로 일부 데이터를 실제로로드해야하는 일부 문제를 해결할 수 있습니다. 또한 String 매개 변수가있는 생성자를 가져 오는 부분은 동일한 데이터 소스에서 전달 된 매개 변수를 사용하여 더 유연하게 만들 수 있습니다. 즉, 파일에서 사용하려는 생성자 서명과 사용할 값 목록을 가져옵니다. 이렇게하면 첫 번째 매개 변수가 문자열이고 첫 번째 개체와 함께 문자열로 캐스팅됩니다. 다음 객체는 Integer 등이지만 프로그램 실행에 따라 이제 File 객체를 먼저 얻은 다음 Double 등을 얻습니다.

이러한 방식으로 이러한 경우를 고려하고 즉석에서 다소 "동적"캐스팅을 수행 할 수 있습니다.

이것이 Google 검색에서 계속 나타나기 때문에 누구나 도움이되기를 바랍니다.


0

최근에이 작업도해야한다고 느꼈지만 코드를 깔끔하게 만들고 더 나은 OOP를 사용하는 다른 방법을 찾았습니다.

각각 특정 메서드를 구현하는 많은 형제 클래스가 doSomething()있습니다. 이 메서드에 액세스하려면 먼저 해당 클래스의 인스턴스가 있어야하지만 모든 형제 클래스에 대한 슈퍼 클래스를 만들었으므로 이제 슈퍼 클래스에서 메서드에 액세스 할 수 있습니다.

아래에서는 "동적 캐스팅"에 대한 두 가지 대안을 보여줍니다.

// Method 1.
mFragment = getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum));
switch (mUnitNum) {
case 0:
    ((MyFragment0) mFragment).sortNames(sortOptionNum);
    break;
case 1:
    ((MyFragment1) mFragment).sortNames(sortOptionNum);
    break;
case 2:
    ((MyFragment2) mFragment).sortNames(sortOptionNum);
    break;
}

현재 사용하는 방법,

// Method 2.
mSuperFragment = (MySuperFragment) getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum));
mSuperFragment.sortNames(sortOptionNum);

동적으로 캐스팅 할 필요가 없습니다. doSomething () 메서드를 사용하는 수퍼 클래스와 doSomething () 메서드를 구현하는 하위 클래스를 갖는 공식은 OOP, 다형성의 주요 목적 중 하나입니다. 객체 foo = new Integer ( "1"); foo.toString ()은 1을 반환합니다. Object에 할당되었지만 Integer이므로 그대로 동작합니다. toString 메소드를 실행하면 Integer 구현이 실행됩니다. 다형성.
George Xavier

0

내가 매우 유용하고 비슷한 요구를 경험하는 사람에게 가능할 수있는 것을 게시 할 것이라고 생각했습니다.

다음 메서드는 컨트롤러가 반환 될 때마다 개체 b 문의 개체 x 인스턴스를 캐스트하지 않고 작성하는 것을 방지하기 위해 JavaFX 응용 프로그램에 대해 작성한 방법입니다.

public <U> Optional<U> getController(Class<U> castKlazz){
    try {
        return Optional.of(fxmlLoader.<U>getController());
    }catch (Exception e){
        e.printStackTrace();
    }
    return Optional.empty();
}

컨트롤러를 얻기위한 메서드 선언은 다음과 같습니다.

public <T> T getController()

클래스 개체를 통해 내 메서드에 전달 된 유형 U를 사용하면 get 컨트롤러 메서드로 전달되어 반환 할 개체 유형을 알릴 수 있습니다. 잘못된 클래스가 제공되고 예외가 발생하면 옵 셔널 객체가 반환되며,이 경우 우리가 확인할 수있는 빈 옵 셔널이 반환됩니다.

이것은 메소드에 대한 최종 호출의 모습입니다 (반환 된 선택적 객체가있는 경우 소비자가

getController(LoadController.class).ifPresent(controller->controller.onNotifyComplete());

0

다이내믹 캐스팅을 위해 이것을 시도하십시오. 작동합니다 !!!

    String something = "1234";
    String theType = "java.lang.Integer";
    Class<?> theClass = Class.forName(theType);
    Constructor<?> cons = theClass.getConstructor(String.class);
    Object ob =  cons.newInstance(something);
    System.out.println(ob.equals(1234));
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.