PROPOSAL: Method and Field Literals
David Goodenough
david.goodenough at linkchoose.co.uk
Thu Mar 12 03:57:59 PDT 2009
On Thursday 12 March 2009, Frédéric Martini wrote:
> Hello,
>
>
> Before to start, sorry for my poor english. This is not my spoken language
> :(
>
>
>
> My advice for this proposal : this is a must-have : this will allow to
> use secure-code with reflection !
>
>
>
> But I want to extends this proposal with the support of beans' property.
> In fact, access to field is very limited due to visibility : there are
> more often protected or private...
> For example: we cannot use Exception#message or Exception#cause :(
>
>
> It will be more interresting to use the beans' property notion, by
> exemple with a Property's class like this (this is a scratch exemple,
> with RuntimeException to be replaced by a more precise unchecked
> exception...) :
>
>
>
> -----------------------------------------------------------------------
>
> /**
> * Immutable class
> *
> * @param <B> The Bean's type
> * @param <T> The property's type
> */
> final class Property<B,T> {
>
> /** Name of the property */
> private final String name;
> /** Read-method of the property */
> private final Method get;
> /** Write-method of the property */
> private final Method set;
>
> /**
> * Private Constructor
> * @see Property#getReadableProperty(Class, Class, String)
> * @see Property#getWritableProperty(Class, Class, String)
> */
> private Property(String name, Method get, Method set) {
> this.name = name;
> this.get = get;
> this.set = set;
> }
>
> /**
> * @return the property name
> */
> public String name() {
> return this.name;
> }
>
> /**
> * Call the get-method on the specified instance.
> */
> public T get(B beans) {
> try {
> @SuppressWarnings("unchecked")
> T result = (T) this.get.invoke(beans);
> return result;
> } catch (Exception e) {
> throw new RuntimeException("get()", e);
> }
> }
>
> /**
> * @return true if this property is writable (have write-method)
> */
> public boolean isWritable() {
> return this.set!=null;
> }
>
> /**
> * Call the set-method on the specified instance.
> */
> public void set(B beans, T value) {
> try {
> this.set.invoke(beans, value);
> } catch (Exception e) {
> throw new RuntimeException("set()", e);
> }
> }
>
> /**
> * Find an create an Property.
> * @param <B> The Bean's type
> * @param <T> The property's type
> * @param beanClass The Bean's type
> * @param propertyClass The property's type
> * @param propertyName The property's name
> * @return A Property for the specified beans-property
> * @throws RuntimeException if the property cannot be found
> */
> private static <B,T> Property<B,T> findProperty(Class<B> beanClass,
> Class<T> propertyClass, String propertyName) {
> try {
> for (PropertyDescriptor descriptor :
> Introspector.getBeanInfo(beanClass).getPropertyDescriptors() ) {
> if (descriptor.getName().equals(propertyName) &&
> propertyClass.equals(descriptor.getPropertyType())) {
> return new Property<B,T>(descriptor.getName(),
> descriptor.getReadMethod(), descriptor.getWriteMethod());
> }
> }
> } catch (IntrospectionException e) {
> throw new RuntimeException("Property not found : " + propertyName, e);
> }
> throw new RuntimeException("Property not found : " + propertyName);
> }
>
> /**
> * Get a read/write property
> * @param <B> The Bean's type
> * @param <T> The property's type
> * @param beanClass The Bean's type
> * @param propertyClass The property's type
> * @param propertyName The property's name
> * @return A Property for the specified beans-property, with get/set
> method * @throws RuntimeException if the property cannot be found, or is
> not writable */
> public static <B,T> Property<B,T> getWritableProperty(Class<B>
> beanClass, Class<T> propertyClass, String propertyName) {
> Property<B,T> property = findProperty(beanClass, propertyClass,
> propertyName); if (!property.isWritable()) {
> throw new RuntimeException("Non-writable property : " + propertyName);
> }
> return property;
> }
>
> /**
> * Get a read-only property.
> * The return type is Property<B,? extends T> in order to forbid the
> use of the set() method
> * @param <B> The Bean's type
> * @param <T> The property's type
> * @param beanClass The Bean's type
> * @param propertyClass The property's type
> * @param propertyName The property's name
> * @return A Property for the specified beans-property, with only get
> method * @throws RuntimeException if the property cannot be found
> */
> public static <B,T> Property<B,? extends T>
> getReadableProperty(Class<B> beanClass, Class<T> propertyClass, String
> propertyName) {
> return findProperty(beanClass, propertyClass, propertyName);
> }
> }
>
> -----------------------------------------------------------------------
>
>
>
>
> Property.getWritableProperty() return a property with read/write
> access, and Property.getReadableProperty() return a property with
> read-only access, declared as Property<B,? extends T> in order to have
> compile-time error if the set method is used...
>
>
>
> Exemple of use :
>
> -----------------------------------------------------------------------
>
> public class MyObject {
>
> private final String name;
> private int value;
>
> public MyObject(String name) {
> this.name = name;
> }
>
> public String getName() {
> return name;
> }
> public int getValue() {
> return value;
> }
> public void setValue(int value) {
> this.value = value;
> }
>
>
> public static void main(String[] args) {
> MyObject beans = new MyObject("name");
>
> Property<MyObject, Integer> valueProperty =
> Property.getWritableProperty(MyObject.class, int.class, "value");
> int value = valueProperty.get(beans); // OK
> valueProperty.set(beans, 100); // OK
>
> Property<MyObject, ? extends String> nameProperty =
> Property.getReadableProperty(MyObject.class, String.class, "name");
> String name = nameProperty.get(beans); // OK
> nameProperty.set(beans, "hello"); // Compile error (is not applicable...)
> }
> }
>
> -----------------------------------------------------------------------
>
>
> My proposal is to use a literal in order to replace the static method
> call (unsafe because based on a String) with a checked literral :
>
> So this :
>
> -----------------------------------------------------------------------
> Property<MyObject, Integer> valueProperty =
> Property.getWritableProperty(MyObject.class, int.class, "value");
> Property<MyObject, ? extends String> nameProperty =
> Property.getReadableProperty(MyObject.class, String.class, "name");
> -----------------------------------------------------------------------
>
> May be remplaced with something like (or equivalent) :
> -----------------------------------------------------------------------
> Property<MyObject, Integer> value = MyObject#value;
> Property<MyObject, ? extends String> name = MyObject#name;
> -----------------------------------------------------------------------
>
> (We can use the same notation that the Field literral, based on
> context, or any other syntaxe...)
>
>
> MyObject#value is matched to Property<MyObject, Integer> because the
> MyObject's class have a property called "value", with a getter
> (getValue()) and a setter (setValue()).
> MyObject#name is matched to Property<MyObject, ? extends String>
> because the name's property only have a getter and no-setter !
>
>
> Of course the literral MyObject#value will be checked at compile time,
> to produce compile-time error on bad usage.
>
> This will allow the literral to be more useful and usable with almost
> all classes :
> -----------------------------------------------------------------------
> Field message = Throwable#message; // ERROR : message don't exist or
> is private :(
> Property<Throwable, ? extends String> message = Throwable#message; // OK
> -----------------------------------------------------------------------
>
>
> What do you think about this ?
>
>
>
> Thanks for reading ;)
>
> Fred,
Fred,
This is a lot like my lightweight properties proposal. It only covers fields
(not methods) but your Property class and mine have a lot in common.
David
More information about the coin-dev
mailing list