2015-07-29 29 views
10

Trong ứng dụng của tôi, tôi gặp phải vấn đề khi một getter trong một lớp được mặc định trong một giao diện (tính năng Java 8), không có thuộc tính Java Beans như một kết quả. I E. cho phương pháp gọi bình thường nó hoạt động giống như một phương pháp tiêu chuẩn, nhưng để truy cập thông qua "tài sản" nó đột nhiên cư xử khác nhau ...Phương thức mặc định giao diện Java 8 dường như không khai báo thuộc tính

Đây là một trường hợp thử nghiệm:

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

Kết quả:

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) 

Câu hỏi: tôi có làm gì sai hoặc là một lỗi trong Java? Có một cách giải quyết khác hơn là không bao giờ sử dụng các phương thức mặc định (cho getters/setters) trong trường hợp bạn có thể cần truy cập chúng như là một "thuộc tính" tại một số điểm sau đó?

Tôi luôn ghét các đặc tính Java "theo quy ước" có xu hướng phá vỡ vì bạn hắt hơi theo cách sai.

+3

Dường như điều này được bao phủ bởi [JDK-8071693] (https://bugs.openjdk.java.net/browse/JDK-8071693), chưa được khắc phục trong bất kỳ bản phát hành JDK nào. –

+0

Vâng, thực sự. Hy vọng họ sửa chữa nó. – doublep

+0

Tôi cũng bị cắn bởi lỗi này. Lỗi OpenJDK được lên kế hoạch cho Java 9 có nghĩa là chúng ta sẽ phải đợi đến ít nhất là tháng 9 năm 2016 để sửa lỗi. Trong thời gian chờ đợi, tôi sẽ chỉ cần tạo một phương thức ủy nhiệm trong lớp cần thuộc tính. –

Trả lời

2

Điều này dường như thực sự là một thiếu sót sai lầm trong Đậu Introspector. Dưới đây là một cách giải quyết khác hơn là không sử dụng default phương pháp:

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

Thẳng thắn, điều này là bất tiện hơn không bao giờ sử dụng các phương pháp mặc định để bắt đầu. – doublep

+1

Hmm, bạn nói bạn ghét "tính chất theo quy ước", vậy thì vấn đề với beaninfo rõ ràng là gì? Nhưng dù sao, Java 8 cho phép bạn tạo các đối tượng giống như thuộc tính dựa trên đặc tính với các lớp đơn, vì vậy tôi không thấy điểm của các công cụ dựa trên Phản chiếu cho điều đó. Tuy nhiên, cách thay thế duy nhất là đợi cho Oracle sửa lỗi đó… – Holger

+0

Holger: Tôi muốn các thuộc tính rõ ràng, nhưng nếu Java có cú pháp sane cho chúng, không hack thứ gì đó trên cơ chế hiện tại. Một vấn đề lớn với phương pháp của bạn là tất cả mọi thứ được lan truyền trên các địa điểm khác nhau, vì vậy bạn có getters/setters ở một nơi, thông tin đậu ở nơi khác, v.v. – doublep

0

Một cách giải quyết nhanh chóng:

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

Nó không hữu ích trong trường hợp của tôi, vì tôi cần các thuộc tính hiển thị từ Java EL. I E. Tôi không kiểm soát nơi họ được truy cập. – doublep

0

Tôi không biết nếu câu trả lời của tôi sẽ rất hữu ích, nhưng tôi đã giải quyết tương tự vấn đề bằng cách sử dụng BeanUtils.getPropertyDescriptors(clazz) từ Spring. Nó hiểu các phương thức mặc định.

Các vấn đề liên quan