如何在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 

您可以像私有void foo(Class c)一样传递Class类型,并像foo(String.class)一样使用
Michael Bavin 2010年

Answers:


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()和两个具体的实现foo(),然后只需调用foo()您已经实现的实现即可。另一种方法可能是Map<Class<?>, Action>,您可以通过致电actions.get(cls)。这很容易与接口和具体实现结合使用:actions.get(cls).foo()


我将向该问题添加需求的详细信息。

我决定现在使用简单版本,因为有问题的类型将始终是原始的(int,string等)。但是,我一定会记住oop方法。谢谢。

String不是Java中的原始语言;)
BalusC,2010年

@Padawan-仍然不够继续。BalusC使用Map的答案就足够了。您接受它是正确的。
duffymo

您也可以Class<?> cls在比较之外的其他方面使用。虽然无法编写:cls value = (cls) aCollection.iterator().next();您可以调用cls.cast(aCollection.iterator()。next()); javadoc类
dlamblin

17

我有一个类似的问题,因此我在下面提出了一个完整的可运行的答案。我需要做的是将一个类(C)传递给一个不相关的类的对象(O),并让该对象(O)在我要它们时向我发出类(C)的新对象。

下面的示例显示了如何完成此操作。您可以使用弹丸类的任何子类型(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>>()。弹丸类应该是接口,并且序列对于您的示例不是必需的,
dlamblin 2011年

1
我想出的唯一原因是读取类名可能很有用。pastebin.com/v9UyPtWT但是,您可以使用一个枚举。pastebin.com/Nw939Js1
dlamblin 2011年

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。这样做的唯一方法是什么?

4
“每当您发现自己编写的代码形式为:“如果对象的类型为T1,则执行某些操作,但是如果对象的类型为T2,则执行其他操作,”请打自己一下。javapractices.com/topic/TopicAction.do?Id=31

@盛宴:感谢您的链接。我想我知道我在说什么,但是在这里我没有任何对象的实例。我希望foo为int与字符串生成不同的字符串。我是否必须创建从其他对象派生的对象的两个实例才能完成操作?在这种情况下可能会大材小用?

我决定现在使用简单版本,因为有问题的类型将始终是原始的(int,string等)。但是,我一定会记住oop方法。谢谢。

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)什么用途有用吗?
robertotomás16

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.