Как передать тип как параметр метода в 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));
}

Есть ли способ в Java без передачи экземпляра типа t?
Или мне нужно использовать собственные константы int или перечисление?
Или есть способ лучше?

Изменить: вот требование для foo: на
основе типа t он генерирует другую короткую строку xml.
Код в if / else будет очень маленьким (одна или две строки) и будет использовать некоторые переменные частного класса.


источник
Вы можете передать тип Class, например, private void foo (Class c), и использовать, например, foo (String.class)
Майкл Бавин

Ответы:

109

Вы могли бы сдать Class<T>.

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

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

Обновление : способ ООП зависит от функциональных требований. Лучшим вариантом будет определение интерфейса foo()и реализация двух конкретных реализаций, foo()а затем просто вызов foo()той реализации, которая у вас под рукой. Другой способ - Map<Class<?>, Action>это позвонить actions.get(cls). Это легко сочетать с интерфейсом и конкретными реализациями: actions.get(cls).foo().

BalusC
источник
Я добавлю подробности требования к вопросу.
Я решил пока использовать простую версию, поскольку рассматриваемые типы всегда будут примитивными (int, string и т. Д.). Тем не менее, я обязательно буду помнить об этом. Благодарю.
Stringне является примитивом в Java;)
BalusC
@Padawan - все еще недостаточно, чтобы продолжать. Ответ от BalusC с использованием Map достаточно. Вы правы, что приняли это.
duffymo
Также вы можете использовать Class<?> clsчто-то другое, кроме сравнения. Пока вы не можете писать: cls value = (cls) aCollection.iterator().next();вы можете вызвать cls.cast (aCollection.iterator (). Next ()); Class javadoc
dlamblin
17

У меня был аналогичный вопрос, поэтому я дал полный рабочий ответ ниже. Мне нужно было передать класс (C) объекту (O) несвязанного класса и заставить этот объект (O) выдавать мне новые объекты класса (C), когда я их просил.

Пример ниже показывает, как это делается. Существует класс MagicGun, который вы загружаете с любым подтипом класса Projectile (Pebble, Bullet или NuclearMissle). Интересно то, что вы загружаете его подтипами 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
1
Единственная причина, по которой я мог придумать, - это то, что чтение имени класса может быть полезно. pastebin.com/v9UyPtWT Но тогда вы можете использовать перечисление. pastebin.com/Nw939Js1
dlamblin
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>

Не знаю, имел ли ты это в виду.

Duffymo
источник
Сейчас только int и string. Какой способ сделать это?
4
«Каждый раз, когда вы обнаруживаете, что пишете код формы», если объект имеет тип T1, тогда делайте что-нибудь, но если это тип T2, то делайте что-нибудь еще, «ударьте себя». javapractices.com/topic/TopicAction.do?Id=31
Pool,
@The Feast: спасибо за ссылку. Я думаю, что понимаю, о чем говорится, но здесь у меня нет экземпляра какого-либо объекта. Я хочу, чтобы foo сгенерировал другую строку для int и строки. Придется ли мне создать два экземпляра объектов, производных от какого-то другого объекта, чтобы сделать это так, как надо? Это может быть излишним для этого случая?
Я решил пока использовать простую версию, поскольку рассматриваемые типы всегда будут примитивными (int, string и т. Д.). Тем не менее, я обязательно буду помнить об этом. Благодарю.
1
@Padawan, извините, я не хотел быть снисходительным - комментарий duffymo напомнил мне эту цитату, и все!
Pool
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)
Ghallio
источник