Java'da bir türü yöntem parametresi olarak iletme


85

Java'da, bir türü parametre olarak nasıl iletebilirsiniz (veya değişken olarak bildirebilirsiniz)?

Türün bir örneğini iletmek istemiyorum ama türün kendisinin (örneğin, int, String, vb.)

C # 'da şunu yapabilirim:

private void foo(Type t)
{
    if (t == typeof(String)) { ... }
    else if (t == typeof(int)) { ... }
}

private void bar()
{
    foo(typeof(String));
}

Java'da t türünün bir örneğini geçmenin bir yolu var mı ?
Yoksa kendi int sabitlerimi veya numaramı mı kullanmalıyım?
Yoksa daha iyi bir yol var mı?

Düzenleme: İşte foo gereksinimi:
T türüne bağlı olarak, farklı bir kısa, xml dizesi oluşturur.
İf / else içindeki kod çok küçük olacaktır (bir veya iki satır) ve bazı özel sınıf değişkenlerini kullanacaktır.

java  types 

Class tipini private void foo (Class c) gibi geçebilir ve foo (String.class) gibi kullanabilirsiniz
Michael Bavin

Yanıtlar:


109

Bir girebilirsin Class<T>.

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

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

Güncelleme : OOP yöntemi, işlevsel gereksinime bağlıdır. En iyi bahis, bir arayüz tanımlama foo()ve uygulayan iki somut uygulama olacaktır.foo() ve ardından foo()elinizdeki uygulamayı çağırmaktır . Başka bir yol da Map<Class<?>, Action>arayabileceğiniz bir yol olabilir actions.get(cls). Bu arabirim ve somut uygulamaları ile kombine edilmesi kolay olan: actions.get(cls).foo().


Sorunun ayrıntılarını soruya ekleyeceğim.

Şimdilik basit sürümle devam etmeye karar verdim çünkü söz konusu türler her zaman ilkel (int, string, vb.) Ancak, oop yolunu kesinlikle aklımda tutacağım. Teşekkürler.

StringJava'da bir ilkel değildir;)
BalusC

@Padawan - devam etmek için hala yeterli değil. Harita kullanarak BalusC'un cevabı yeterlidir. Kabul etmekte haklısın.
duffymo

Ayrıca Class<?> clskarşılaştırma dışında başka bir şeyde de kullanabilirsiniz . cls value = (cls) aCollection.iterator().next();Yazamadığınız halde : cls.cast (aCollection.iterator (). Next ()); Class javadoc
dlamblin

17

Benzer bir sorum vardı, bu yüzden aşağıda eksiksiz bir çalıştırılabilir cevap buldum. Yapmam gereken şey, ilgisiz bir sınıfın bir nesnesine (O) bir sınıf (C) geçirmek ve bu nesnenin (O), onları istediğimde (C) sınıfının yeni nesnelerini bana geri göndermesini sağlamaktır.

Aşağıdaki örnek bunun nasıl yapıldığını göstermektedir. Projectile sınıfının herhangi bir alt türü (Pebble, Bullet veya NuclearMissle) ile yüklediğiniz bir MagicGun sınıfı vardır. İlginç olan, onu Mermi'nin alt türleriyle yüklemeniz, ancak bu türdeki gerçek nesnelerle değil. MagicGun, çekim zamanı geldiğinde gerçek nesneyi oluşturur.

Çıktı

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

Kod

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
Bir türü bir metoda geçirmeyi demo yapmaya çalıştığınızı anlıyorum ve genel olarak örneği seviyorum, ancak bunun ortaya çıkardığı basit soru, neden sihirli silahı yeni mermi örnekleriyle doldurmuyorsunuz, gibi konuları karmaşıklaştırmak yerine bu? Ayrıca uyarıların düzeltilmesini de bastırmayın List<Class<? extends Projectile>> projectiles = new ArrayList<Class<? extends Projectile>>(). Projectile sınıfı bir arayüz olmalı ve örneğiniz için seri gerekli değildir,
dlamblin

1
Ortaya çıkabilmemin tek nedeni, sınıf adını okumanın yararlı olabileceğidir. pastebin.com/v9UyPtWT Ama o zaman bir enum kullanabilirsiniz. pastebin.com/Nw939Js1
dlamblin

10

Oh, ama bu çirkin, nesne yönelimli olmayan kod. "If / else" ve "typeof" u gördüğünüz an, çok biçimlilik düşünmelisiniz. Bu gitmenin yanlış yolu. Sanırım jenerikler burada senin arkadaşın.

Kaç türle uğraşmayı planlıyorsunuz?

GÜNCELLEME:

Sadece String ve int hakkında konuşuyorsanız, işte bunu yapmanın bir yolu. XmlGenerator arayüzüyle başlayın ("foo" ile yeterli):

package generics;

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

Ve somut uygulama 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(">");
    }
}

Bunu çalıştırırsam şu sonucu alırım:

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

Aklındaki bu muydu bilmiyorum.


Şu anda yalnızca int ve string. Bunu yapmanın en iyi yolu nedir?

4
Eğer nesne T1 tipindeyse "ne zaman kendini formun kodunu yazarken bulsan", o zaman bir şeyler yap, ama eğer T2 tipindeyse başka bir şey yap, "kendini tokatla". javapractices.com/topic/TopicAction.do?Id=31
Havuz

@ The Feast: bağlantı için teşekkürler. Sanırım ne söylendiğini anlıyorum ama burada herhangi bir nesnenin bir örneğine sahip değilim. Foo'nun bir int ve bir dizge için farklı bir dizge oluşturmasını istiyorum. Bunu oop yöntemiyle yapmak için başka bir nesneden türetilmiş iki nesne örneği oluşturmam gerekir mi? Bu dava için fazla olabilir mi?

Şimdilik basit sürümle devam etmeye karar verdim çünkü söz konusu türler her zaman ilkel (int, string, vb.) Ancak, oop yolunu kesinlikle aklımda tutacağım. Teşekkürler.

1
@Padawan, üzgünüm, küçümsemek istemedim - duffymo'nun yorumu bana bu alıntıyı hatırlattı!
Havuz

9

Geçmelisin Class...

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

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

Neden bir türü değil de bir sınıfı geçmelisiniz? gibi imzaların method_foo(Type fooableType)daha az yararlı olmasının bir nedeni var method_foo(Class<?> fooableClass)mı?
roberto tomás

5

Türü geçmek istiyorsanız, Java'daki eşdeğerden daha fazla

java.lang.Class

Zayıf yazılmış bir yöntem kullanmak istiyorsanız, o zaman basitçe

java.lang.Object

ve ilgili operatör

instanceof

Örneğin

private void foo(Object o) {

  if(o instanceof String) {

  }

}//foo

Bununla birlikte, Java'da sınıf olmayan ilkel türler vardır (örn. Sizin örneğinizden int), bu nedenle dikkatli olmanız gerekir.

Asıl soru, burada gerçekten neyi başarmak istediğinizdir, aksi takdirde cevap vermek zordur:

Yoksa daha iyi bir yol var mı?


0

Türü temsil eden java.lang.Class örneğini, yani

private void foo(Class cls)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.