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

在 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 常量或 enum?
还是有更好的办法?

编辑: 以下是 foo 的要求:
基于 t 类型,它生成一个不同的简短 xml 字符串。
If/else 中的代码将非常小(一行或两行) ,并且将使用一些私有类变量。

204872 次浏览

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().

You can pass an instance of java.lang.Class that represents the type, i.e.

private void foo(Class cls)

You should pass a Class...

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


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

Oh, but that's ugly, non-object-oriented code. The moment you see "if/else" and "typeof", you should be thinking polymorphism. This is the wrong way to go. I think generics are your friend here.

How many types do you plan to deal with?

UPDATE:

If you're just talking about String and int, here's one way you might do it. Start with the interface XmlGenerator (enough with "foo"):

package generics;


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

And the concrete implementation 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(">");
}
}

If I run this, I get the following result:

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

I don't know if this is what you had in mind.

If you want to pass the type, than the equivalent in Java would be

java.lang.Class

If you want to use a weakly typed method, then you would simply use

java.lang.Object

and the corresponding operator

instanceof

e.g.

private void foo(Object o) {


if(o instanceof String) {


}


}//foo

However, in Java there are primitive types, which are not classes (i.e. int from your example), so you need to be careful.

The real question is what you actually want to achieve here, otherwise it is difficult to answer:

Or is there a better way?

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.

The example below shows how this is done. There is a MagicGun class that you load with any subtype of the Projectile class (Pebble, Bullet or NuclearMissle). The interesting is you load it with subtypes of Projectile, but not actual objects of that type. The MagicGun creates the actual object when it's time to shoot.

The Output

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

The Code

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;
}