Як передавати тип як параметр методу в 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 буде дуже маленьким (один або два рядки) і використовуватиме деякі змінні приватного класу.

java  types 

Ви можете передати тип класу як private void foo (клас 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().


До запитання додам деталі вимоги.

Зараз я вирішив піти на просту версію, оскільки типи, про які йдеться, завжди будуть примітивними (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, який ви завантажуєте з будь-яким підтипом класу Снаряд (Pebble, Bullet або NuclearMissle). Цікаво, що ви завантажуєте його з підтипами снаряда, але не з реальними об’єктами цього типу. 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

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. Яким би був oop-спосіб зробити це?

4
"Будь-коли ви виявите, що пишете код форми", якщо об'єкт типу T1, тоді зробіть щось, але якщо це тип T2, то зробіть щось інше, "ляпайте себе". javapractices.com/topic/TopicAction.do?Id=31
Пул

@ Свято: дякую за посилання. Здається, я розумію, про що йдеться, але тут я не маю екземпляра жодного об’єкта. Я хочу, щоб foo генерував інший рядок для int проти рядка. Чи потрібно мені створювати два екземпляри об'єктів, похідних від якогось іншого об'єкта, щоб зробити це oop-способом? Це може бути надмірним для цього випадку?

Зараз я вирішив піти на просту версію, оскільки типи, про які йдеться, завжди будуть примітивними (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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.