Come passare un tipo come parametro del metodo in Java


85

In Java, come puoi passare un tipo come parametro (o dichiararlo come variabile)?

Non voglio passare un'istanza del tipo ma il tipo stesso (es. Int, String, ecc.).

In C #, posso farlo:

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

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

Esiste un modo in Java senza passare un'istanza di tipo t?
O devo usare le mie costanti int o enum?
O c'è un modo migliore?

Modifica: ecco il requisito per foo: in
base al tipo t, genera una stringa xml breve diversa.
Il codice in if / else sarà molto piccolo (una o due righe) e utilizzerà alcune variabili di classe privata.

java  types 

Puoi passare il tipo di classe come private void foo (Class c) e utilizzare come foo (String.class)
Michael Bavin

Risposte:


109

You could pass a Class<T> in.

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

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

Update: the OOP way depends on the functional requirement. Best bet would be an interface defining foo() and two concrete implementations implementing foo() and then just call foo() on the implementation you've at hand. Another way may be a Map<Class<?>, Action> which you could call by actions.get(cls). This is easily to be combined with an interface and concrete implementations: actions.get(cls).foo().


I'll add details of the requirement to the question.

I've decided to go with the simple version for now since the types in question will always be primitive (int, string, etc). However, I will definitely keep the oop way in mind. Thanks.

String is not a primitive in Java ;)
BalusC

@Padawan - still not enough to go on. The answer from BalusC using Map is sufficient. You're right to accept it.
duffymo

Inoltre puoi usare Class<?> clsqualcosa di diverso dal confronto. Anche se non puoi scrivere: cls value = (cls) aCollection.iterator().next();puoi chiamare cls.cast (aCollection.iterator (). Next ()); Class javadoc
dlamblin

17

I had a similar question, so I worked up a complete runnable answer below. What I needed to do is pass a class (C) to an object (O) of an unrelated class and have that object (O) emit new objects of class (C) back to me when I asked for them.

L'esempio seguente mostra come eseguire questa operazione. Esiste una classe MagicGun che si carica con qualsiasi sottotipo della classe Projectile (Pebble, Bullet o NuclearMissle). La cosa interessante è che lo carichi con i sottotipi di Proiettile, ma non con oggetti reali di quel tipo. MagicGun crea l'oggetto reale quando è il momento di sparare.

Il risultato

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

Il codice

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
Capisco che stai provando a fare una demo passando un tipo a un metodo, e in generale mi piace l'esempio, ma la semplice domanda che questo solleva è perché non dovresti semplicemente caricare la pistola magica con nuove istanze di proiettili piuttosto che complicare cose come Questo? Inoltre non sopprimere gli avvisi risolverli con List<Class<? extends Projectile>> projectiles = new ArrayList<Class<? extends Projectile>>(). La classe Projectile dovrebbe essere un'interfaccia e il seriale non è necessario per il tuo esempio,
dlamblin

1
L'unico motivo per cui ho potuto pensare è che leggere il nome della classe potrebbe essere utile. pastebin.com/v9UyPtWT Ma poi, potresti usare un enum. pastebin.com/Nw939Js1
dlamblin

10

Oh, ma questo è un codice brutto, non orientato agli oggetti. Nel momento in cui vedi "if / else" e "typeof", dovresti pensare al polimorfismo. Questa è la strada sbagliata. Penso che i generici siano tuoi amici qui.

Quanti tipi prevedi di trattare?

AGGIORNARE:

Se stai parlando solo di String e int, ecco un modo in cui potresti farlo. Inizia con l'interfaccia XmlGenerator (basta con "foo"):

package generics;

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

E l'implementazione concreta 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(">");
    }
}

Se lo eseguo, ottengo il seguente risultato:

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

Non so se questo è quello che avevi in ​​mente.


Al momento solo int e string. Quale sarebbe il modo oop per farlo?

4
"Ogni volta che ti ritrovi a scrivere il codice del modulo" se l'oggetto è di tipo T1, fai qualcosa, ma se è di tipo T2, allora fai qualcos'altro, "schiaffeggia te stesso". javapractices.com/topic/TopicAction.do?Id=31
Pool

@ The Feast: grazie per il link. Penso di capire cosa viene detto, ma qui non ho un'istanza di alcun oggetto. Voglio che foo generi una stringa diversa per un int rispetto a una stringa. Dovrei creare due istanze di oggetti derivati ​​da qualche altro oggetto per farlo in modo oop? Potrebbe essere eccessivo per questo caso?

Per ora ho deciso di utilizzare la versione semplice poiché i tipi in questione saranno sempre primitivi (int, string, ecc.). Tuttavia, terrò sicuramente a mente il modo oop. Grazie.

1
@Padawan, scusa non volevo essere condiscendente - il commento di duffymo mi ha ricordato quella citazione è tutto!
Piscina

9

Dovresti passare un Class...

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

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

Perché dovresti superare un corso e non un tipo? c'è qualche motivo per cui firme come questa method_foo(Type fooableType)sono meno utili di method_foo(Class<?> fooableClass)?
roberto tomás

5

Se vuoi passare il tipo, allora l'equivalente in Java sarebbe

java.lang.Class

Se vuoi usare un metodo debolmente tipizzato, allora dovresti semplicemente usare

java.lang.Object

e l'operatore corrispondente

instanceof

per esempio

private void foo(Object o) {

  if(o instanceof String) {

  }

}//foo

Tuttavia, in Java ci sono tipi primitivi, che non sono classi (cioè int dal tuo esempio), quindi devi stare attento.

La vera domanda è cosa vuoi effettivamente ottenere qui, altrimenti è difficile rispondere:

O c'è un modo migliore?


0

Puoi passare un'istanza di java.lang.Class che rappresenta il tipo, ad es

private void foo(Class cls)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.