java: Bir değişkenin bir türden diğerine dinamik dönüşümünü nasıl yapabilirim?


85

Bir Java değişkeni için dinamik döküm yapmak istiyorum, döküm türü farklı bir değişkende saklanıyor.

Bu normal döküm:

 String a = (String) 5;

İstediğim bu:

 String theType = 'String';
 String a = (theType) 5;

Bu mümkün mü ve eğer mümkünse nasıl? Teşekkürler!

Güncelleme

Bir sınıfı HashMapaldığım bir sınıfla doldurmaya çalışıyorum .

Bu kurucu:

public ConnectParams(HashMap<String,Object> obj) {

    for (Map.Entry<String, Object> entry : obj.entrySet()) {
        try {
            Field f =  this.getClass().getField(entry.getKey());                
            f.set(this, entry.getValue()); /* <= CASTING PROBLEM */
        } catch (NoSuchFieldException ex) {
            log.error("did not find field '" + entry.getKey() + '"');
        } catch (IllegalAccessException ex) {
            log.error(ex.getMessage());         
        }
    }

}

Buradaki sorun, sınıf değişkenlerinin bir kısmının türde olmasıdır Doubleve 3 sayısı alınırsa, onu olarak görür Integerve benim tür sorunum var.


Bu hiç mantıklı değil. Değişken adının bir dizgeyi bir dizgeye çevirmek için bir tür olmasını mı istiyorsunuz? Ne?
cletus

3
Bir cevap bilmiyorum ama korkarım bu bakım cehenneme dönüşebilir ... Sadece Java öğreniyorum ama böyle yaklaşım gerektiren durumlardan kaçınırım. Eminim ki ne yaparsanız yapın daha iyi bir şekilde uygulanabilir ... sadece benim 2 sentim.
Sejanus

Tamam, başarmaya çalıştığım şey hakkında daha fazla bilgi vereceğim.
ufk

ayrıca aşağıdaki cevabımı güncelledi!
user85421

Yanıtlar:


14

Güncellemenizle ilgili olarak, bunu Java'da çözmenin tek yolu, çok sayıda ifve elseve instanceofifadeleri içeren tüm durumları kapsayan bir kod yazmaktır . Yapmaya çalıştığınız şey, dinamik dillerle programlamaya alışkın gibi görünüyor. Statik dillerde, yapmaya çalıştığınız şey neredeyse imkansızdır ve muhtemelen yapmaya çalıştığınız şey için tamamen farklı bir yaklaşım seçecektir. Statik diller dinamik olanlar kadar esnek değildir :)

En iyi Java uygulaması örnekleri, BalusC'nin (ie ObjectConverter) yanıtı ve Adapteraşağıdaki Andreas_D'nin (ie ) yanıtıdır .


Bu mantıklı değil

String a = (theType) 5;

türünün astatik olarak olması zorunludur, bu Stringnedenle bu statik türe dinamik bir atama yapmak mantıklı değildir.

Not: Örneğinizin ilk satırı şu şekilde yazılabilir, Class<String> stringClass = String.class;ancak yine de stringClassdeğişkenleri dönüştürmek için kullanamazsınız .


Umarım yayınladığım güncelleme ne yapmaya çalıştığımı açıklar. php geçmişinden geliyorum, bu yüzden belki bu şeyin java'da başarılması mümkün değil.
ufk

Kesinlikle, Java'da o kadar dinamik olamazsınız, güncellememe de bakın.
akuhn

Aşağıdaki BalusC cevabına bakın, bu gitmeniz gereken uzunluk (ve acı) ...
akuhn

Bunun geç olduğunu biliyorum ama sanırım [tür] a = (tür) bir_objeyi kastettiğini düşünüyorum; bu anlamsız çünkü sadece Nesne a = bir_nesne ince yapabilirsiniz
George Xavier

120

Evet, Yansıma kullanarak mümkündür

Object something = "something";
String theType = "java.lang.String";
Class<?> theClass = Class.forName(theType);
Object obj = theClass.cast(something);

ancak sonuçta ortaya çıkan nesnenin bir değişken Objecttüründe kaydedilmesi gerektiğinden, bu pek mantıklı değil . Değişkenin belirli bir sınıfa ait olmasına ihtiyacınız varsa, sadece o sınıfa çevirebilirsiniz.

Belirli bir sınıfı elde etmek istiyorsanız, Numberörneğin:

Object something = new Integer(123);
String theType = "java.lang.Number";
Class<? extends Number> theClass = Class.forName(theType).asSubclass(Number.class);
Number obj = theClass.cast(something);

ama bunu yapmanın hala bir anlamı yok, sadece atabilirsiniz Number.

Bir nesnenin çevrilmesi hiçbir şeyi DEĞİŞTİRMEZ; bu sadece derleyicinin onu ele alma şeklidir .
Böyle bir şey yapmanın tek nedeni, nesnenin belirli bir sınıfın veya herhangi bir alt sınıfının bir örneği olup olmadığını kontrol etmektir, ancak bu, instanceofveya kullanılarak daha iyi yapılır Class.isInstance().

Güncelleme

Son güncellemenize göre asıl sorun, Integeriçinde HashMapbir Double. Ne bu durumda yapabileceği, alanın türünü kontrol etmek ve kullanmak olduğunu xxxValue()yöntemleriniNumber

...
Field f =  this.getClass().getField(entry.getKey());
Object value = entry.getValue();
if (Integer.class.isAssignableFrom(f.getType())) {
    value = Integer.valueOf(((Number) entry.getValue()).intValue());
} else if (Double.class.isAssignableFrom(f.getType())) {
    value = Double.valueOf(((Number) entry.getValue()).doubleValue());
} // other cases as needed (Long, Float, ...)
f.set(this, value);
...

(yanlış türe sahip olma fikrini beğenip beğenmediğimden emin değilim Map)


22

Bunun için bir çeşit yazmanız gerekecek ObjectConverter. Bu, hem dönüştürmek istediğiniz nesneye sahipseniz hem de dönüştürmek istediğiniz hedef sınıfı biliyorsanız yapılabilir. Bu özel durumda hedef sınıfı elde edebilirsiniz Field#getDeclaringClass().

Burada böyle bir örneğini bulabilirsiniz ObjectConverter. Size temel fikri vermeli. Daha fazla dönüştürme olasılığı istiyorsanız, istediğiniz bağımsız değişken ve dönüş türü ile ona daha fazla yöntem ekleyin.


@BalusC - ObjectConverter kodunu ilginç buluyorum, bunun için kullanım durumlarını açıklar mısınız?
srini.venigalla

Yapılandırmanın yerine kuralın tercih edildiği ve kaynak türünün hedef türle eşleşmediği durumlarda kullanışlıdır. Bunu 2-3 yıl önce (hobi amaçlı) ORM ve MVC çerçevelerimde kullandım. Ayrıca blog makalesinin ana metnine bakın.
BalusC

12

Bunu Class.cast(), sağlanan parametreyi sahip olduğunuz sınıf örneğinin türüne dinamik olarak çeviren yöntemi kullanarak yapabilirsiniz . Belirli bir alanın sınıf örneğini elde etmek için, söz konusu alandaki getType()yöntemi kullanırsınız . Aşağıda bir örnek verdim, ancak tüm hata işlemeyi atladığını ve değiştirilmeden kullanılmaması gerektiğini unutmayın.

public class Test {

    public String var1;
    public Integer var2;
}

public class Main {

    public static void main(String[] args) throws Exception {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("var1", "test");
        map.put("var2", 1);

        Test t = new Test();

        for (Map.Entry<String, Object> entry : map.entrySet()) {
            Field f = Test.class.getField(entry.getKey());

            f.set(t, f.getType().cast(entry.getValue()));
        }

        System.out.println(t.var1);
        System.out.println(t.var2);
    }
}

1
Ya girdi türü, alan türünün bir üst türü değilse? Daha sonra gerçekten programlı olarak dönüştürmeniz gerekecek.
BalusC

7

Aşağıdaki gibi basit bir castMethod yazabilirsiniz.

private <T> T castObject(Class<T> clazz, Object object) {
  return (T) object;
}

Metodunda şöyle kullanmalısın

public ConnectParams(HashMap<String,Object> object) {

for (Map.Entry<String, Object> entry : object.entrySet()) {
    try {
        Field f =  this.getClass().getField(entry.getKey());                
        f.set(this, castObject(entry.getValue().getClass(), entry.getValue()); /* <= CASTING PROBLEM */
    } catch (NoSuchFieldException ex) {
        log.error("did not find field '" + entry.getKey() + '"');
    } catch (IllegalAccessException ex) {    
        log.error(ex.getMessage());          
    }    
}

}

Evet, soruyu soranın istediği cevabın bu olduğunu düşünüyorum. Sadece bir <?> Sınıfı alıyor ve bir örneği "?" Sınıfına dönüştürmek istiyor. Varsayılan olarak java bunu desteklemez. Ancak <T> kullanarak bunu yapabiliriz.
ruiruige1991

@ ruiruige1991 Bu yanlış. Bu durumda T bir jeneriktir. Jenerikler çalışma sırasında hiçbir şey yapmaz. (T) falan, tür silme nedeniyle çalışma sırasında sadece (Nesne) falan olurdu. Kısacası, jenerikler -> derleme zamanıdır ve çalışma süresi boyunca hiçbir etkisi yoktur. Dinamik -> çalışma zamanı ve jenerikler -> derleme zamanı olduğundan, jenerikler işe yaramaz.
George Xavier

5

Çalışır ve yaklaşımınız için ortak bir model bile vardır: Adaptör modeli . Ancak elbette, (1) java ilkellerini nesnelere dökmek için çalışmaz ve (2) sınıf uyarlanabilir olmalıdır (genellikle özel bir arabirim uygulayarak).

Bu modelle şöyle bir şey yapabilirsiniz:

Wolf bigBadWolf = new Wolf();
Sheep sheep = (Sheep) bigBadWolf.getAdapter(Sheep.class);

ve Wolf sınıfındaki getAdapter yöntemi:

public Object getAdapter(Class clazz) {
  if (clazz.equals(Sheep.class)) {
    // return a Sheep implementation
    return getWolfDressedAsSheep(this);
  }

  if (clazz.equals(String.class)) {
    // return a String
    return this.getName();
  }

  return null; // not adaptable
}

Senin için özel fikir - bu imkansız. Çevrim için bir String değeri kullanamazsınız.


2

Senin sorunun "dinamik döküm" eksikliği değil. Öğesine yayın Integeryapmak Doublehiç mümkün değil. Görünüşe göre, Java'ya tek tip bir nesne, muhtemelen uyumsuz tipte bir alan vermek ve bir şekilde türler arasında nasıl dönüşüm yapılacağını otomatik olarak bulmasını sağlamak istiyorsunuz.

Bu tür şeyler, Java ve IMO gibi çok iyi yazılmış bir dil için çok iyi nedenlerden ötürü anathema.

Aslında ne yapmaya çalışıyorsun? Tüm bu yansıma kullanımı oldukça şüpheli görünüyor.


@name: önermeye devam ettiğiniz düzenlemeyle ilgili olarak: ilkel değerlerden değil, sarmalayıcı sınıflarından (büyük harf ve kod olarak stil ile belirtilir) bahsettiğimi ve bunlar arasında geçiş yapmak kesinlikle mümkün olmadığını unutmayın.
Michael Borgwardt

1

Bunu yapma. Bunun yerine doğru şekilde parametreleştirilmiş bir kurucuya sahip olun. Bağlantı parametrelerinin seti ve türleri zaten sabittir, bu nedenle bunu dinamik olarak yapmanın bir anlamı yoktur.


1

Değeri ne olursa olsun, çoğu komut dosyası oluşturma dili (Perl gibi) ve statik olmayan derleme zamanı dilleri (Pick gibi) otomatik çalışma zamanı dinamik String'den (nispeten rastgele) nesne dönüşümlerini destekler. Bu, Java'da da tür güvenliğini kaybetmeden gerçekleştirilebilir ve statik olarak yazılan diller, dinamik döküm ile kötü şeyler yapan bazı diğer dillerin kötü yan etkileri OLMADAN sağlar. Şüpheli matematik yapan bir Perl örneği:

print ++($foo = '99');  # prints '100'
print ++($foo = 'a0');  # prints 'a1'

Java'da bu, "çapraz döküm" adını verdiğim bir yöntemi kullanarak daha iyi gerçekleştirilir (IMHO). Çapraz çevrim ile yansıma, aşağıdaki statik yöntemle dinamik olarak keşfedilen yapıcıların ve yöntemlerin tembel yüklü bir önbelleğinde kullanılır:

Object fromString (String value, Class targetClass)

Ne yazık ki, Class.cast () gibi yerleşik Java yöntemleri bunu String'den BigDecimal'e veya String'den Tamsayıya ya da destekleyen sınıf hiyerarşisinin olmadığı başka herhangi bir dönüştürme için yapmaz. Benim açımdan önemli olan, bunu başarmak için tamamen dinamik bir yol sağlamaktır - bunun için önceki referansın doğru yaklaşım olduğunu düşünmüyorum - her dönüşümü kodlamak zorunda. Basitçe söylemek gerekirse, eğer yasal / mümkünse, uygulama dizgeden çevrimdir.

Dolayısıyla çözüm, aşağıdakilerden birinin halka açık Üyelerini arayan basit bir düşüncedir:

STRING_CLASS_ARRAY = (yeni Sınıf [] {String.class});

a) Üye üye = targetClass.getMethod (method.getName (), STRING_CLASS_ARRAY); b) Üye üye = targetClass.getConstructor (STRING_CLASS_ARRAY);

Tüm ilkellerin (Tamsayı, Uzun, vb.) Ve tüm temellerin (BigInteger, BigDecimal, vb.) Ve hatta java.regex.Pattern'in tümünün bu yaklaşımla kaplandığını göreceksiniz. Bunu, bazı daha sıkı kontrollerin gerekli olduğu büyük miktarda keyfi String değeri girdisinin olduğu üretim projelerinde önemli bir başarıyla kullandım. Bu yaklaşımda, bir yöntem yoksa veya yöntem çağrıldığında bir istisna atılır (çünkü bu, bir BigDecimal'e sayısal olmayan bir girdi veya bir Desen için yasa dışı RegEx gibi geçersiz bir değerdir), hedef sınıfın içsel mantığı.

Bunun bazı dezavantajları var:

1) Yansımayı iyi anlamanız gerekir (bu biraz karmaşıktır ve acemiler için değildir). 2) Bazı Java sınıfları ve gerçekten de 3. taraf kitaplıkları (şaşırtıcı) düzgün kodlanmamıştır. Yani, girdi olarak tek bir dizge argümanı alan ve hedef sınıfın bir örneğini döndüren yöntemler vardır, ancak bu sizin düşündüğünüz gibi değildir ... Integer sınıfını düşünün:

static Integer getInteger(String nm)
      Determines the integer value of the system property with the specified name.

Yukarıdaki yöntemin, ilkel tamsayıları saran nesneler olarak Tamsayılarla hiçbir ilgisi yoktur. Yansıma, bunu bir Dizeden hatalı bir şekilde bir Tamsayı oluşturmak için olası bir aday olarak bulacaktır.Kod çözme, değer ve yapıcı Üyeler - bunların tümü girdi verileriniz üzerinde gerçekten kontrol sahibi olmadığınız ancak sadece yapmak istediğiniz çoğu keyfi Dize dönüşümü için uygundur. bir Tamsayı olup olmadığını bilin.

Bu tür nesnelerin örneklerini oluşturmak geçersiz giriş değerleri nedeniyle yukarıdaki düzeltmek için, İstisnalar atmak yöntemleri arayan iyi bir başlangıç olmalıdır bir özel durum atar. Ne yazık ki, uygulamalar, İstisnaların işaretli olarak beyan edilip edilmediğine göre değişir. Integer.valueOf (String), örneğin kontrol edilmiş bir NumberFormatException oluşturur, ancak Pattern.compile () istisnaları yansıma aramaları sırasında bulunmaz. Yine, bu dinamik "çapraz döküm" yaklaşımının bir başarısızlığı değil, nesne oluşturma yöntemlerinde istisna bildirimleri için çok standart olmayan bir uygulama olduğunu düşünüyorum.

Yukarıdakilerin nasıl uygulandığına dair daha fazla ayrıntı isteyen biri varsa, bana bildirin, ancak bu çözümün çok daha esnek / genişletilebilir ve tür güvenliğinin iyi kısımlarını kaybetmeden daha az kodla olduğunu düşünüyorum. Elbette "verilerinizi bilmek" her zaman en iyisidir, ancak çoğumuzun bulduğu gibi, bazen sadece yönetilmeyen içeriğin alıcılarıyız ve onu doğru şekilde kullanmak için elimizden gelenin en iyisini yapmak zorundayız.

Şerefe.


1

Yani bu eski bir gönderi, ancak buna bir katkıda bulunabileceğimi düşünüyorum.

Her zaman böyle bir şey yapabilirsiniz:

package com.dyna.test;  

import java.io.File;  
import java.lang.reflect.Constructor;  

public class DynamicClass{  

  @SuppressWarnings("unchecked")  
  public Object castDynamicClass(String className, String value){  
    Class<?> dynamicClass;  

    try  
    {  
      //We get the actual .class object associated with the specified name  
      dynamicClass = Class.forName(className);  



    /* We get the constructor that received only 
     a String as a parameter, since the value to be used is a String, but we could
easily change this to be "dynamic" as well, getting the Constructor signature from
the same datasource we get the values from */ 


      Constructor<?> cons =  
        (Constructor<?>) dynamicClass.getConstructor(new Class<?>[]{String.class});  

      /*We generate our object, without knowing until runtime 
 what type it will be, and we place it in an Object as 
 any Java object extends the Object class) */  
      Object object = (Object) cons.newInstance(new Object[]{value});  

      return object;  
    }  
    catch (Exception e)  
    {  
      e.printStackTrace();  
    }  
    return null;  
  }  

  public static void main(String[] args)  
  {   
    DynamicClass dynaClass = new DynamicClass();  

    /* 
 We specify the type of class that should be used to represent 
 the value "3.0", in this case a Double. Both these parameters 
 you can get from a file, or a network stream for example. */  
    System.out.println(dynaClass.castDynamicClass("java.lang.Double", "3.0"));  

    /* 
We specify a different value and type, and it will work as 
 expected, printing 3.0 in the above case and the test path in the one below, as the Double.toString() and 
 File.toString() would do. */  
    System.out.println(dynaClass.castDynamicClass("java.io.File", "C:\\testpath"));  
  }  

Tabii ki, bu diğer dillerde (örneğin Python) olduğu gibi gerçekten dinamik bir çevrim değildir, çünkü java statik olarak yazılmış bir dildir. Ancak bu, bazı tanımlayıcılara bağlı olarak bazı verileri farklı şekillerde yüklemeniz gereken bazı sınır durumlarını çözebilir. Ayrıca, String parametresine sahip bir kurucu aldığınız kısım, bu parametrenin aynı veri kaynağından geçirilmesiyle muhtemelen daha esnek hale getirilebilir. Yani bir dosyadan, kullanmak istediğiniz yapıcı imzasını ve kullanılacak değerlerin listesini alırsınız, bu şekilde, örneğin, ilk parametre bir Dize'dir, ilk nesne ile onu bir Dize olarak çevirerek, sonraki nesne bir Tamsayıdır, vb, ancak bir şekilde programınızın yürütülmesi sırasında, şimdi önce bir File nesnesi, sonra bir Double nesnesi vb. elde edersiniz.

Bu şekilde, bu durumları hesaba katabilir ve hareket halindeyken biraz "dinamik" bir döküm yapabilirsiniz.

Umarım bu, Google aramalarında ortaya çıkmaya devam ettiği için bu herkese yardımcı olur.


0

Son zamanlarda bunu da yapmam gerektiğini hissettim, ancak daha sonra muhtemelen kodumun daha düzgün görünmesini sağlayan ve daha iyi OOP kullanan başka bir yol buldum.

Her biri belirli bir yöntemi uygulayan birçok kardeş sınıfım var doSomething(). Bu yönteme erişmek için, önce o sınıfın bir örneğine sahip olmam gerekirdi, ancak tüm kardeş sınıflarım için bir üst sınıf oluşturdum ve artık yönteme süper sınıftan erişebiliyorum.

Aşağıda "dinamik yayınlama" için iki alternatif yol gösteriyorum.

// Method 1.
mFragment = getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum));
switch (mUnitNum) {
case 0:
    ((MyFragment0) mFragment).sortNames(sortOptionNum);
    break;
case 1:
    ((MyFragment1) mFragment).sortNames(sortOptionNum);
    break;
case 2:
    ((MyFragment2) mFragment).sortNames(sortOptionNum);
    break;
}

ve şu anda kullandığım yöntem,

// Method 2.
mSuperFragment = (MySuperFragment) getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum));
mSuperFragment.sortNames(sortOptionNum);

Asla dinamik olarak döküm yapmanıza gerek yok. DoSomething () yöntemiyle bir üst sınıfa ve doSomething () yöntemini uygulayan alt sınıflara sahip olma formülünüz, OOP'nin ana amaçlarından biridir, polimorfizm. Nesne foo = new Tamsayı ("1"); foo.toString (), 1 değerini döndürür. Object'e atanmış olmasına rağmen, bir Tamsayıdır ve bu nedenle böyle davranır. ToString yöntemini çalıştırmak, Tamsayı uygulamasını çalıştırır. Polimorfizm.
George Xavier

0

Oldukça yararlı bulduğum ve benzer ihtiyaçları yaşayan biri için mümkün olabilecek bir şey göndereceğimi düşündüm.

Aşağıdaki yöntem, JavaFX uygulamam için dönüştürme zorunluluğundan kaçınmak ve ayrıca denetleyici her döndürüldüğünde nesne x nesnesi b ifadelerinin örneğini yazmamak için yazdığım bir yöntemdi.

public <U> Optional<U> getController(Class<U> castKlazz){
    try {
        return Optional.of(fxmlLoader.<U>getController());
    }catch (Exception e){
        e.printStackTrace();
    }
    return Optional.empty();
}

Denetleyiciyi elde etmek için yöntem bildirimi

public <T> T getController()

Yöntemime sınıf nesnesi aracılığıyla iletilen U türünü kullanarak, hangi tür nesnenin döndürüleceğini söylemek için get controller yöntemine iletilebilir. Yanlış sınıf verilmesi durumunda isteğe bağlı bir nesne döndürülür ve bir istisna oluşur ve bu durumda kontrol edebileceğimiz boş bir isteğe bağlı döndürülür.

Yönteme yapılan son çağrı böyle görünüyordu (döndürülen isteğe bağlı nesnenin mevcut olması durumunda bir Tüketici

getController(LoadController.class).ifPresent(controller->controller.onNotifyComplete());

0

Dinamik Yayın için bunu deneyin. Çalışacak!!!

    String something = "1234";
    String theType = "java.lang.Integer";
    Class<?> theClass = Class.forName(theType);
    Constructor<?> cons = theClass.getConstructor(String.class);
    Object ob =  cons.newInstance(something);
    System.out.println(ob.equals(1234));
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.