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に方法はありますか?
または、独自のint定数または列挙型を使用する必要がありますか?
それとももっと良い方法はありますか?

編集:fooの要件は次のとおりです。
タイプtに基づいて、異なる短いxml文字列を生成します。
if / elseのコードは非常に小さく(1行または2行)、いくつかのプライベートクラス変数を使用します。

java  types 

private void foo(Class c)のようなクラスタイプを渡し、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()、2つの具体的な実装を実装してfoo()から、foo()手元にある実装を呼び出すことです。別の方法は、Map<Class<?>, Action>で呼び出すことができるものかもしれませんactions.get(cls)。これは、インターフェースおよび具体的な実装と簡単に組み合わせることができますactions.get(cls).foo()


要件の詳細を質問に追加します。

問題の型は常にプリミティブ(int、stringなど)であるため、今のところ単純なバージョンを使用することにしました。しかし、私は間違いなくおっとの方法を心に留めておきます。ありがとう。

StringJavaのプリミティブではありません;)
BalusC 2010

@ Padawan-まだ十分ではありません。Mapを使用したBalusCからの回答で十分です。あなたはそれを受け入れる権利があります。
duffymo 2010

またClass<?> cls、比較以外にも使用できます。書くcls value = (cls) aCollection.iterator().next();ことはできませんが、cls.cast(aCollection.iterator()。next());を呼び出すことができます。クラスのjavadoc
dlamblin

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>>()。で修正してください。Projectileクラスはインターフェイスである必要があり、例ではシリアルは必要ありません
dlamblin 2011

1
私が思いついた唯一の理由は、クラス名を読むことが役立つかもしれないということです。pastebin.com/v9UyPtWTしかし、その後、列挙型を使用することができます。pastebin.com/Nw939Js1
dlamblin

10

ああ、でもそれは醜い、オブジェクト指向ではないコードです。「if / else」と「typeof」が表示された瞬間、ポリモーフィズムを考えているはずです。これは間違った方法です。ジェネリックはここであなたの友達だと思います。

いくつのタイプを扱う予定ですか?

更新:

Stringとintについて話しているだけの場合は、次の1つの方法があります。インターフェイス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のみです。これを行うためのoopの方法は何でしょうか?

4
「オブジェクトがT1型の場合は何かを実行し、T2型の場合は別のことを実行する」という形式のコードを記述していることに気付いたときはいつでも、自分を平手打ちしてください。javapractices.com/topic/TopicAction.do?Id=31
プール

@The Feast:リンクをありがとう。私は何が言われているのか理解していると思いますが、ここにはオブジェクトのインスタンスがありません。fooがintと文字列に対して異なる文字列を生成するようにしたい。それをおっと行うには、他のオブジェクトから派生したオブジェクトの2つのインスタンスを作成する必要がありますか?この場合はやり過ぎかもしれませんか?

問題の型は常にプリミティブ(int、stringなど)であるため、今のところ単純なバージョンを使用することにしました。しかし、私は間違いなくおっとの方法を心に留めておきます。ありがとう。

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)ますか?
トマス・ロベルト

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.