2015-07-29 25 views
10

Nella mia applicazione mi imbatto in un problema che quando un getter in una classe è predefinito in una sola interfaccia (funzione Java 8), non esiste una proprietà Java Beans come risultato. Cioè per il metodo normale invocazione funziona proprio come un metodo standard, ma per l'accesso tramite "proprietà" si comporta in modo diverso ... improvvisamenteIl metodo di default dell'interfaccia Java 8 non sembra dichiarare la proprietà

Qui è un banco di prova:

import java.beans.Introspector; 
import java.util.Arrays; 
import java.util.stream.Collectors; 
import org.apache.commons.beanutils.PropertyUtils; 

public class test 
{ 
    public static void main (String[] arguments) throws Exception 
    { 
     // Normal language-level invocation, works fine. 
     System.out.println (new Bean1().getFoo()); 
     System.out.println (new Bean2().getFoo()); 

     // Printing Java Beans properties; Bean2 doesn't have 'foo' property... 
     System.out.println (Arrays.stream (Introspector.getBeanInfo (Bean1.class).getPropertyDescriptors()) 
          .map ((property) -> property.getName()) 
          .collect (Collectors.joining (", "))); 
     System.out.println (Arrays.stream (Introspector.getBeanInfo (Bean2.class).getPropertyDescriptors()) 
          .map ((property) -> property.getName()) 
          .collect (Collectors.joining (", "))); 

     // First call behaves as expected, second dies with exception. 
     System.out.println (PropertyUtils.getProperty (new Bean1(), "foo")); 
     System.out.println (PropertyUtils.getProperty (new Bean2(), "foo")); 
    } 

    public interface Foo 
    { 
     default String getFoo() 
     { 
      return "default foo"; 
     } 
    } 

    public static class Bean1 implements Foo 
    { 
     @Override 
     public String getFoo() 
     { 
      return "special foo"; 
     } 
    } 

    public static class Bean2 implements Foo 
    { } 
} 

Risultato:

special foo 
default foo 
class, foo 
class 
special foo 
Exception in thread "main" java.lang.NoSuchMethodException: Unknown property 'foo' on class 'class test$Bean2' 
     at org.apache.commons.beanutils.PropertyUtilsBean.getSimpleProperty(PropertyUtilsBean.java:1257) 
     at org.apache.commons.beanutils.PropertyUtilsBean.getNestedProperty(PropertyUtilsBean.java:808) 
     at org.apache.commons.beanutils.PropertyUtilsBean.getProperty(PropertyUtilsBean.java:884) 
     at org.apache.commons.beanutils.PropertyUtils.getProperty(PropertyUtils.java:464) 
     at test.main(test.java:21) 

Domande: faccio qualcosa di sbagliato o si tratta di un bug in Java? C'è una soluzione alternativa che non usare mai metodi predefiniti (per getter/setter) nel caso in cui potessi aver bisogno di accedervi come "proprietà" ad un certo punto dopo?

Ho sempre odiato Java "proprietà per convenzione" che tendono a rompersi perché si starnutisce nel modo sbagliato.

+3

Sembra che questo sia coperto da [JDK-8071693] (https://bugs.openjdk.java.net/browse/JDK-8071693), non ancora corretto in nessuna versione di JDK. –

+0

Sì, davvero. Spero che lo risolvano. – doublep

+0

Sono stato morso da questo bug. Il bug di OpenJDK è programmato per Java 9, il che significa che dovremo aspettare almeno fino a settembre 2016 per essere corretto. Nel frattempo vorrei semplicemente creare un metodo delegato nella classe che ha bisogno della proprietà. –

risposta

2

Questa sembra essere un'omissione errata nei Fagioli Introspector. Ecco una soluzione diversa che non utilizzano metodi default:

public static void main (String[] arguments) throws Exception { 
    testBean(new Bean1()); 
    System.out.println(); 
    testBean(new Bean2()); 
} 
static void testBean(Object bean) throws Exception { 
    PropertyDescriptor[] pd 
     = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors(); 
    System.out.println(Arrays.stream(pd) 
     .map(PropertyDescriptor::getName).collect(Collectors.joining(", "))); 
    for(PropertyDescriptor p: pd) 
     System.out.println(p.getDisplayName()+": "+p.getReadMethod().invoke(bean)); 
} 
public interface Foo { 
    default String getFoo() { 
     return "default foo"; 
    } 
} 
public static class Bean1 implements Foo { 
    @Override 
    public String getFoo() { 
     return "special foo"; 
    } 
} 
public static class Bean2BeanInfo extends SimpleBeanInfo { 
    private final BeanInfo ifBeanInfo; 
    public Bean2BeanInfo() throws IntrospectionException { 
     ifBeanInfo=Introspector.getBeanInfo(Foo.class); 
    } 
    @Override 
    public BeanInfo[] getAdditionalBeanInfo() { 
     return new BeanInfo[]{ifBeanInfo}; 
    } 
} 
public static class Bean2 implements Foo { } 
class, foo 
class: class helper.PropTest$Bean1 
foo: special foo 
class, foo 
class: class helper.PropTest$Bean2 
foo: default foo 
+0

Francamente, questo è più sconveniente che mai usando i metodi predefiniti per cominciare. – doublep

+1

Hmm, hai detto che odi "proprietà per convenzione", quindi qual è il problema con un beaninfo esplicito? Ma in ogni caso, Java 8 consente di creare oggetti basati su proprietà basati su funzioni con single-liner, quindi non vedo il punto degli strumenti basati su Reflection. Tuttavia, l'unica alternativa è aspettare che Oracle risolva il problema ... – Holger

+0

Holger: Vorrei proprietà esplicite, ma se Java avesse una sintassi sana per loro, non avrebbe hackerato qualcosa in aggiunta al meccanismo esistente. Un grosso problema con il tuo metodo è che tutto è distribuito su diversi luoghi, quindi hai getter/setter in un posto, informazioni sui bean altrove ecc. – doublep

0

Una soluzione rapida:

try { 
    return PropertyUtils.getProperty(bean, property); 
} 
catch (NoSuchMethodException e) { 
    String getMethod = "get" + property.substring(0, 1).toUpperCase() + property.substring(1); 
    return MethodUtils.invokeMethod(bean, getMethod, new Object[]{}); 
} 
+0

Non aiuta nel mio caso, perché ho bisogno di proprietà per essere visibile da Java EL. Cioè Non controllo il luogo in cui sono accessibili. – doublep

0

Non so se la mia risposta sarà utile, ma ho risolto simile problema utilizzando BeanUtils.getPropertyDescriptors(clazz) dalla primavera. Comprende i metodi predefiniti.