Programing

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

lottogame 2020. 10. 29. 07:46
반응형

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


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이 수신되면 정수로 간주되고 유형 문제가 있다는 것입니다.


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

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


말이 안 돼요

String a = (theType) 5;

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

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


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

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

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

주어진 클래스를 얻으려면 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().

최신 정보

마지막 업데이트에 따르면 실제 문제는 HashMap에 Double에 할당되어야하는 Integer가 있다는 것입니다. 이 경우 할 수있는 일은 필드의 유형을 확인하고 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);
...

(지도에서 잘못된 유형을 갖는 아이디어가 마음에 드는지 확실하지 않음)


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

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


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);
    }
}

아래와 같이 간단한 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());          
    }    
}

}

작동하며 접근 방식에 대한 일반적인 패턴 인 어댑터 패턴도 있습니다. 그러나 물론 (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
}

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


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

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

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


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


그만한 가치가있는 대부분의 스크립팅 언어 (예 : 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 () 예외를 찾을 수 없습니다. 다시 말하지만,이 동적 "크로스 캐스팅"접근 방식의 실패가 아니라 객체 생성 메서드에서 예외 선언을위한 매우 비표준 구현이라고 생각합니다.

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

건배.


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

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

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"));  
  }  

Of course, this is not really dynamic casting, as in other languages (Python for example), because java is a statically typed lang. However, this can solve some fringe cases where you actually need to load some data in different ways, depending on some identifier. Also, the part where you get a constructor with a String parameter could be probably made more flexible, by having that parameter passed from the same data source. I.e. from a file, you get the constructor signature you want to use, and the list of values to be used, that way you pair up, say, the first parameter is a String, with the first object, casting it as a String, next object is an Integer, etc, but somehwere along the execution of your program, you get now a File object first, then a Double, etc.

In this way, you can account for those cases, and make a somewhat "dynamic" casting on-the-fly.

Hope this helps anyone as this keeps turning up in Google searches.


I recently felt like I had to do this too, but then found another way which possibly makes my code look neater, and uses better OOP.

I have many sibling classes that each implement a certain method doSomething(). In order to access that method, I would have to have an instance of that class first, but I created a superclass for all my sibling classes and now I can access the method from the superclass.

Below I show two ways alternative ways to "dynamic casting".

// 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;
}

and my currently used method,

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

Just thought I would post something that I found quite useful and could be possible for someone who experiences similar needs.

The following method was a method I wrote for my JavaFX application to avoid having to cast and also avoid writing if object x instance of object b statements every time the controller was returned.

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

The method declaration for obtaining the controller was

public <T> T getController()

By using type U passed into my method via the class object, it could be forwarded to the method get controller to tell it what type of object to return. An optional object is returned in case the wrong class is supplied and an exception occurs in which case an empty optional will be returned which we can check for.

This is what the final call to the method looked like (if present of the optional object returned takes a Consumer

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

Try this for Dynamic Casting. It will work!!!

    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));

참고URL : https://stackoverflow.com/questions/2127318/java-how-can-i-do-dynamic-casting-of-a-variable-from-one-type-to-another

반응형