Java에서 유형을 메소드 매개 변수로 전달하는 방법


85

Java에서 유형을 매개 변수로 전달 (또는 변수로 선언)하려면 어떻게해야합니까?

유형 의 인스턴스 가 아니라 유형 자체 (예 : int, String 등) 를 전달하고 싶지 않습니다 .

C #에서는 다음과 같이 할 수 있습니다.

private void foo(Type t)
{
    if (t == typeof(String)) { ... }
    else if (t == typeof(int)) { ... }
}

private void bar()
{
    foo(typeof(String));
}

유형 t 의 인스턴스 를 전달하지 않고 Java에서 방법이 있습니까?
아니면 내 정수 상수 나 열거 형을 사용해야합니까?
아니면 더 좋은 방법이 있습니까?

편집 : 다음은 foo에 대한 요구 사항입니다.
유형 t를 기반으로 다른 짧은 xml 문자열을 생성합니다.
if / else의 코드는 매우 작고 (한두 줄) 개인 클래스 변수를 사용합니다.

java  types 

private void foo (Class c)와 같은 Class 유형을 전달하고 foo (String.class)와 같이 사용할 수 있습니다.
Michael Bavin

답변:


109

통과 할 수 Class<T>있습니다.

private void foo(Class<?> cls) {
    if (cls == String.class) { ... }
    else if (cls == int.class) { ... }
}

private void bar() {
    foo(String.class);
}

업데이트 : OOP 방식은 기능 요구 사항에 따라 다릅니다. 가장 좋은 방법은 인터페이스를 정의 foo()하고 두 가지 구체적인 구현을 구현 foo()한 다음 foo()현재 사용중인 구현을 호출 하는 것입니다. 다른 방법은으로 Map<Class<?>, Action>호출 할 수 actions.get(cls)있습니다. 이것은 인터페이스 및 구체적인 구현과 쉽게 결합됩니다 actions.get(cls).foo().


질문에 요구 사항에 대한 세부 정보를 추가하겠습니다.

문제의 유형이 항상 원시 (int, string 등)이기 때문에 지금은 단순 버전을 사용하기로 결정했습니다. 그러나 나는 확실히 oop 방식을 염두에 둘 것입니다. 감사.

String는 자바에서 원시적이지 않습니다.)
BalusC 2010

@Padawan-아직 충분하지 않습니다. Map을 사용하는 BalusC의 답변으로 충분합니다. 당신은 그것을 받아들이는 것이 옳습니다.
duffymo

또한 Class<?> cls비교 이외의 용도로 사용할 수 있습니다 . 쓸 cls value = (cls) aCollection.iterator().next();수는 없지만 cls.cast (aCollection.iterator (). next ()); 호출 할 수 있습니다. 클래스 javadoc
dlamblin 2011-09-26

17

비슷한 질문이 있었기 때문에 아래에서 완전한 실행 가능한 답변을 작성했습니다. 내가해야 할 일은 클래스 (C)를 관련없는 클래스의 객체 (O)에 전달하고 그 객체 (O)가 요청했을 때 클래스 (C)의 새 객체를 내게 다시 내보내도록하는 것입니다.

아래 예는 이것이 어떻게 수행되는지 보여줍니다. Projectile 클래스 (Pebble, Bullet 또는 NuclearMissle)의 하위 유형으로로드하는 MagicGun 클래스가 있습니다. 흥미로운 점은 Projectile의 하위 유형으로로드하지만 해당 유형의 실제 오브젝트는로드하지 않습니다. MagicGun은 촬영할 때 실제 물체를 만듭니다.

출력

You've annoyed the target!
You've holed the target!
You've obliterated the target!
click
click

코드

import java.util.ArrayList;
import java.util.List;

public class PassAClass {
    public static void main(String[] args) {
        MagicGun gun = new MagicGun();
        gun.loadWith(Pebble.class);
        gun.loadWith(Bullet.class);
        gun.loadWith(NuclearMissle.class);
        //gun.loadWith(Object.class);   // Won't compile -- Object is not a Projectile
        for(int i=0; i<5; i++){
            try {
                String effect = gun.shoot().effectOnTarget();
                System.out.printf("You've %s the target!\n", effect);
            } catch (GunIsEmptyException e) {
                System.err.printf("click\n");
            }
        }
    }
}

class MagicGun {
    /**
     * projectiles holds a list of classes that extend Projectile. Because of erasure, it
     * can't hold be a List<? extends Projectile> so we need the SuppressWarning. However
     * the only way to add to it is the "loadWith" method which makes it typesafe. 
     */
    private @SuppressWarnings("rawtypes") List<Class> projectiles = new ArrayList<Class>();
    /**
     * Load the MagicGun with a new Projectile class.
     * @param projectileClass The class of the Projectile to create when it's time to shoot.
     */
    public void loadWith(Class<? extends Projectile> projectileClass){
        projectiles.add(projectileClass);
    }
    /**
     * Shoot the MagicGun with the next Projectile. Projectiles are shot First In First Out.
     * @return A newly created Projectile object.
     * @throws GunIsEmptyException
     */
    public Projectile shoot() throws GunIsEmptyException{
        if (projectiles.isEmpty())
            throw new GunIsEmptyException();
        Projectile projectile = null;
        // We know it must be a Projectile, so the SuppressWarnings is OK
        @SuppressWarnings("unchecked") Class<? extends Projectile> projectileClass = projectiles.get(0);
        projectiles.remove(0);
        try{
            // http://www.java2s.com/Code/Java/Language-Basics/ObjectReflectioncreatenewinstance.htm
            projectile = projectileClass.newInstance();
        } catch (InstantiationException e) {
            System.err.println(e);
        } catch (IllegalAccessException e) {
            System.err.println(e);
        }
        return projectile;
    }
}

abstract class Projectile {
    public abstract String effectOnTarget();
}

class Pebble extends Projectile {
    @Override public String effectOnTarget() {
        return "annoyed";
    }
}

class Bullet extends Projectile {
    @Override public String effectOnTarget() {
        return "holed";
    }
}

class NuclearMissle extends Projectile {
    @Override public String effectOnTarget() {
        return "obliterated";
    }
}

class GunIsEmptyException extends Exception {
    private static final long serialVersionUID = 4574971294051632635L;
}

2
나는 당신이 메소드에 유형을 전달하는 것을 시연하고 있다는 것을 이해하고, 나는 일반적으로 예제를 좋아하지만, 이것이 제기하는 간단한 질문은 왜 당신이 다음과 같은 문제를 복잡하게하기보다는 발사체의 새로운 인스턴스로 마법 총을로드하지 않는가하는 것입니다. 이? 또한 경고를 억제하지 마십시오 List<Class<? extends Projectile>> projectiles = new ArrayList<Class<? extends Projectile>>(). 발사체 클래스는 인터페이스해야하고, 시리얼, 당신의 예를 들어 필요가 없습니다
dlamblin

1
내가 생각 해낼 수있는 유일한 이유는 클래스 이름을 읽는 것이 유용 할 수 있다는 것입니다. pastebin.com/v9UyPtWT 하지만 그러면 열거 형을 사용할 수 있습니다. pastebin.com/Nw939Js1
dlamblin 2011 년

10

오, 그러나 그것은 추악하고 객체 지향적이지 않은 코드입니다. "if / else"및 "typeof"를 보는 순간 다형성을 생각해야합니다. 이것은 잘못된 길입니다. 제네릭은 여기서 당신의 친구라고 생각합니다.

얼마나 많은 유형을 다룰 계획입니까?

최신 정보:

String과 int에 대해 이야기하고 있다면 여기에 할 수있는 한 가지 방법이 있습니다. XmlGenerator 인터페이스로 시작합니다 ( "foo"로 충분 함).

package generics;

public interface XmlGenerator<T>
{
   String getXml(T value);
}

그리고 구체적인 구현 XmlGeneratorImpl :

    package generics;

public class XmlGeneratorImpl<T> implements XmlGenerator<T>
{
    private Class<T> valueType;
    private static final int DEFAULT_CAPACITY = 1024;

    public static void main(String [] args)
    {
        Integer x = 42;
        String y = "foobar";

        XmlGenerator<Integer> intXmlGenerator = new XmlGeneratorImpl<Integer>(Integer.class);
        XmlGenerator<String> stringXmlGenerator = new XmlGeneratorImpl<String>(String.class);

        System.out.println("integer: " + intXmlGenerator.getXml(x));
        System.out.println("string : " + stringXmlGenerator.getXml(y));
    }

    public XmlGeneratorImpl(Class<T> clazz)
    {
        this.valueType = clazz;
    }

    public String getXml(T value)
    {
        StringBuilder builder = new StringBuilder(DEFAULT_CAPACITY);

        appendTag(builder);
        builder.append(value);
        appendTag(builder, false);

        return builder.toString();
    }

    private void appendTag(StringBuilder builder) { this.appendTag(builder, false); }

    private void appendTag(StringBuilder builder, boolean isClosing)
    {
        String valueTypeName = valueType.getName();
        builder.append("<").append(valueTypeName);
        if (isClosing)
        {
            builder.append("/");
        }
        builder.append(">");
    }
}

이것을 실행하면 다음과 같은 결과가 나타납니다.

integer: <java.lang.Integer>42<java.lang.Integer>
string : <java.lang.String>foobar<java.lang.String>

이것이 당신이 염두에 둔 것인지 모르겠습니다.


지금은 int와 string 만 있습니다. 이 작업을 수행하는 방법은 무엇입니까?

4
"객체가 T1 유형이면 어떤 작업을 수행하고 T2 유형이면 다른 작업을 수행하십시오."라는 형식의 코드를 작성하는 자신을 발견 할 때마다 자신을 때리십시오. " javapractices.com/topic/TopicAction.do?Id=31
수영장

@The Feast : 링크 주셔서 감사합니다. 나는 내가 말하는 것을 이해한다고 생각하지만 여기에는 어떤 객체의 인스턴스가 없습니다. foo가 int 대 문자열에 대해 다른 문자열을 생성하기를 원합니다. oop 방식으로 수행하려면 다른 개체에서 파생 된 개체의 두 인스턴스를 만들어야합니까? 이 사건에 대해 과잉일까요?

문제의 유형이 항상 원시 (int, string 등)이기 때문에 지금은 단순 버전을 사용하기로 결정했습니다. 그러나 나는 확실히 oop 방식을 염두에 둘 것입니다. 감사.

1
@Padawan, 미안합니다. 내가 착각하는 것을 의미하지는 않았습니다. duffymo의 의견은 그 인용문이 전부임을 상기시켜주었습니다!

9

당신은 통과해야 Class...

private void foo(Class<?> t){
    if(t == String.class){ ... }
    else if(t == int.class){ ... }
}

private void bar()
{
   foo(String.class);
}

유형이 아닌 클래스를 통과해야하는 이유는 무엇입니까? 같은 서명 method_foo(Type fooableType)이보다 유용하지 않은 이유 가 method_foo(Class<?> fooableClass)있습니까?
roberto tomás

5

유형을 전달하려면 Java에서 동등한 것보다

java.lang.Class

약한 유형의 방법을 사용하려면 간단히

java.lang.Object

및 해당 연산자

instanceof

예 :

private void foo(Object o) {

  if(o instanceof String) {

  }

}//foo

그러나 Java에는 클래스 (예 : 예제의 int)가 아닌 기본 유형이 있으므로주의해야합니다.

진짜 질문은 여기에서 실제로 달성하려는 것이 무엇인지, 그렇지 않으면 대답하기 어렵습니다.

아니면 더 좋은 방법이 있습니까?


0

유형을 나타내는 java.lang.Class의 인스턴스를 전달할 수 있습니다.

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