如何在 Java 中将类型作为方法参数传递

Posted

技术标签:

【中文标题】如何在 Java 中将类型作为方法参数传递【英文标题】:How to pass a type as a method parameter in Java 【发布时间】:2011-01-15 11:41:42 【问题描述】:

在 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 中的代码会非常小(一两行),并且会使用一些私有类变量。

【问题讨论】:

您可以像 private void foo(Class c) 一样传递 Class 类型并像 foo(String.class) 一样使用 【参考方案1】:

你可以传递一个Class<T> in.

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&lt;Class&lt;?&gt;, Action&gt;,您可以拨打actions.get(cls)。这很容易与接口和具体实现相结合:actions.get(cls).foo()

【讨论】:

我将在问题中添加要求的详细信息。 我现在决定使用简单版本,因为所讨论的类型总是原始的(int、string 等)。但是,我一定会牢记 oop 方式。谢谢。 String 在 Java 中不是原语 ;) @Padawan - 还不够继续。 BalusC 使用 Map 的答案就足够了。你接受它是对的。 您也可以使用Class&lt;?&gt; cls 进行比较以外的其他用途。虽然你不能写:cls value = (cls) aCollection.iterator().next(); 你可以调用 cls.cast(aCollection.iterator().next()); Class javadoc【参考方案2】:

你可以传递一个代表类型的 java.lang.Class 的实例,即

private void foo(Class cls)

【讨论】:

【参考方案3】:

你应该传递一个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&lt;?&gt; fooableClass) 有用吗?【参考方案4】:

哦,但那是丑陋的、非面向对象的代码。当您看到“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 方法是什么? “任何时候你发现自己在编写“如果对象是 T1 类型,那么做某事,但如果它是 T2 类型,那么做其他事情”形式的代码,“打自己一巴掌。” javapractices.com/topic/TopicAction.do?Id=31 @The Feast:感谢您的链接。我想我明白在说什么,但在这里我没有任何对象的实例。我希望 foo 为 int 和 string 生成不同的字符串。我是否必须创建两个从其他对象派生的对象实例才能以 oop 方式进行操作?这种情况可能有点过头了? 我现在决定使用简单版本,因为所讨论的类型总是原始的(int、string 等)。但是,我一定会牢记 oop 方式。谢谢。 @Padawan,对不起,我不是故意居高临下的 - duffymo 的评论让我想起了那句话!【参考方案5】:

如果你想传递类型,那么Java中的等价物就是

java.lang.Class

如果你想使用弱类型方法,那么你只需使用

java.lang.Object

以及对应的运算符

instanceof

例如

private void foo(Object o) 

  if(o instanceof String) 

  

//foo

但是,在 Java 中存在原始类型,它们不是类(即您示例中的 int),因此您需要小心。

真正的问题是你在这里真正想要实现什么,否则很难回答:

或者有没有更好的方法?

【讨论】:

【参考方案6】:

我有一个类似的问题,所以我在下面找到了一个完整的可运行答案。我需要做的是将一个类 (C) 传递给一个不相关类的对象 (O),并让该对象 (O) 在我请求它们时将类 (C) 的新对象返回给我。

下面的示例显示了这是如何完成的。您可以使用 Projectile 类的任何子类型(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;

【讨论】:

我知道您正在尝试演示将类型传递给方法,我一般喜欢这个示例,但是这提出的一个简单问题是,您为什么不直接在魔术枪上加载新的抛射物的实例,而不是使这样的事情复杂化?也不要禁止使用List&lt;Class&lt;? extends Projectile&gt;&gt; projectiles = new ArrayList&lt;Class&lt;? extends Projectile&gt;&gt;() 修复它们的警告。 Projectile 类应该是一个接口,并且您的示例不需要序列号, 我能想到的唯一原因是阅读类名可能有用。 pastebin.com/v9UyPtWT 但是,您可以使用枚举。 pastebin.com/Nw939Js1

以上是关于如何在 Java 中将类型作为方法参数传递的主要内容,如果未能解决你的问题,请参考以下文章

如何在Java中将泛型对象作为方法参数传递? [复制]

在Java中将原始类型作为输出参数传递

如何在Java中将对象数组作为参数传递

如何在Java中将类作为参数传递?

如何在oracle中将对象类型的对象作为参数传递

如何在 Python 中将方法作为参数传递