web-dev-qa-db-fra.com

Comment passer un type en tant que paramètre de méthode dans Java

En Java, comment pouvez-vous passer un type en tant que paramètre (ou déclarer en tant que variable)?

Je ne veux pas passer un instance du type mais le type lui-même (par exemple, int, String, etc.).

En C #, je peux faire ceci:

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

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

Y a-t-il un moyen d'entrer Java sans passer un instance de type t?
Ou dois-je utiliser mes propres constantes int ou enum?
Ou existe-t-il un meilleur moyen?

Edit: Voici l'exigence pour foo:
En fonction du type t, il génère une chaîne différente, courte xml.
Le code dans le if/else sera très petit (une ou deux lignes) et utilisera des variables de classe privées.

70
user250781

Vous pouvez passer un Class<T> dans.

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

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

Mise à jour : le moyen OOP dépend de l'exigence fonctionnelle. Le meilleur pari serait une interface définissant foo() et deux implémentations concrètes implémenter foo() et ensuite simplement appeler foo() sur l'implémentation que vous avez sous la main. Vous pouvez également utiliser un Map<Class<?>, Action> que vous pouvez appeler par actions.get(cls). Il est facile de la combiner à une interface et à des implémentations concrètes: actions.get(cls).foo().

96
BalusC

J'avais une question similaire, alors j'ai élaboré une réponse complète et complète ci-dessous. Ce que je devais faire est de passer une classe (C) à un objet (O) d’une classe non apparentée et de lui faire renvoyer de nouveaux objets de la classe (C) lorsque je les ai demandés.

L'exemple ci-dessous montre comment cela est fait. Il existe une classe MagicGun que vous chargez avec n'importe quel sous-type de la classe Projectile (Pebble, Bullet ou NuclearMissle). Ce qui est intéressant, c’est que vous le chargez avec des sous-types de Projectile, mais pas avec des objets de ce type. Le MagicGun crée l'objet réel au moment de tirer.

Le résultat

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

Le code

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;
}
14
JohnnyLambada

Vous devriez passer un Class...

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

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

Oh, mais c'est un code laid, non orienté objet. Au moment où vous voyez "if/else" et "typeof", vous devriez penser au polymorphisme. C'est la mauvaise façon de faire. Je pense que les génériques sont votre ami ici.

Combien de types envisagez-vous de traiter?

MISE À JOUR:

Si vous ne parlez que de String et int, voici une façon de le faire. Commencez par l'interface XmlGenerator (assez avec "foo"):

package generics;

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

Et l'implémentation concrète 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(">");
    }
}

Si je lance ceci, j'obtiens le résultat suivant:

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

Je ne sais pas si c'est ce que vous aviez en tête.

8
duffymo

Si vous voulez passer le type, alors l'équivalent dans Java serait

Java.lang.Class

Si vous souhaitez utiliser une méthode faiblement typée, vous utiliserez simplement

Java.lang.Object

et l'opérateur correspondant

instanceof

par exemple.

private void foo(Object o) {

  if(o instanceof String) {

  }

}//foo

Cependant, dans Java, il existe des types primitifs, qui ne sont pas des classes (c’est-à-dire int de votre exemple)), vous devez donc faire attention.

La vraie question est ce que vous voulez réellement réaliser ici, sinon il est difficile de répondre:

Ou y a-t-il un meilleur moyen?

5
Dieter

Vous pouvez transmettre une instance de Java.lang.Class qui représente le type, c'est-à-dire.

private void foo(Class cls)
0
ghallio