PROPOSAL: Method and Field Literals
Frédéric Martini
frederic.martini at gmail.com
Thu Mar 12 03:41:54 PDT 2009
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,
More information about the coin-dev
mailing list