Balzanka / guava-libraries

Automatically exported from code.google.com/p/guava-libraries
Apache License 2.0
0 stars 0 forks source link

Class for accessing fields #1293

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
It would be great to have a Fields class for accessing java.lang.reflect.Field 
in a fluent way.

For example something like

Iterable<Field> fields = 
Fields.forClass(SomeClass.class).traverse().filter(Predicate)

I'm also thinking about a similar class for Methods.

I've written and attached a sample implementation.

Original issue reported on code.google.com by carsten....@gmail.com on 19 Feb 2013 at 1:10

Attachments:

GoogleCodeExporter commented 9 years ago
I guess I'm not certain how much benefit it has over:

for (Class<?> cls : 
TypeToken.of(SomeClass.class).getTypes().classes().rawTypes()) {
  for (Field field : cls.getDeclaredFields()) {
    if (...) {
      // do it
    }
  }
}

Sure it's not one-liner but is readable enough to me.

Original comment by be...@google.com on 20 Feb 2013 at 1:32

GoogleCodeExporter commented 9 years ago
To be honest i never realized the TypeToken class. But i think that my solution 
still has some benefits.

The readability is increased
(compare 

for(Field f : Fields.forClass(SomeClass.class).traverse().filter(...)) {
.
}

to 

for (Class<?> cls : 
TypeToken.of(SomeClass.class).getTypes().classes().rawTypes()) {
  for (Field field : cls.getDeclaredFields()) {
    if (...) {
    }
  }
}
)

Another example:
I want to get all fields from the class hierachy until a certain superclass is 
reached. 

I'm not sure how i would do it with the TypeToken class (perhaps you can give 
an easy example). In my example (you can see it also in the test class) it 
would look like 

Fields.forClass(C.class).traverseUntil(A.class))

And at least it's in my oppinion a more object oriented way to create a 
reusable fields object

Original comment by carsten....@gmail.com on 20 Feb 2013 at 11:42

GoogleCodeExporter commented 9 years ago
With TypeToken, it would be:

for (Class<?> cls : 
TypeToken.of(SomeClass.class).getTypes().classes().rawTypes()) {
  if (cls == Base.class) {
    break;
  }
  for (Field field : cls.getDeclaredFields()) {
    if (...) {
    }
  }
}

I'm concerned that there may be some number of variants of the pattern such 
that covering them all with a single API will be hard. 

* I may want to loop through methods.
* When looping through methods, maybe interfaces should also be looked at.
* Well, actually interfaces can have fields too (constants), there may be need 
to look into them too.
* I may want to loop through public fields using getFields(), not 
getDeclaredFields().
* I may want to look into nested classes as well?
* I may loop until a certain class-level annotation is absent or present.

When we add a utility, we want either high utility (that it can simplify the 
code a lot, not a bit), or high ubiquity (that it's needed very often), or 
ideally both. 

With the potential big API surface needed to cover these variant use cases, I'm 
not yet convinced the use case is compelling.

Especially with traverse*() methods, it's not self-explanatory about which 
variant of the use case it handles; is it declared fields or public fields 
only? Dos it look into interfaces? If it does, what does traverseUntil(A.class) 
mean if A is an interface? How about synthetic fields?

Original comment by be...@google.com on 20 Feb 2013 at 3:08

GoogleCodeExporter commented 9 years ago
* As the name suggest working with methods are use cases for the Fields class.
* Fetching constants would be possible by adding a includeConstants() method.
* Looping only through public fields is possible through the filter method
* I wouldn't consider nested classes as they are not part of the class hierarchy

You are right about the self-explanatory. What do you think about a 
traverse(Predicate<Class>) method to overcome this problem?

Original comment by carsten....@gmail.com on 27 Feb 2013 at 2:09

GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
Yes. methods don't belong to Fields. I brought it up just to say that we need a 
wider API surface than just Fields to handle all possible cases. While with the 
TypeToken loop, it's up to users to compose things together.

includeConstants(() would mean by default static final fields aren't included. 
That may be surprising to some who expect them to be included. And the API has 
no reliable way to tell if a static final field is constant (unless it checks 
the field name to be ALL_CAPS). 

So if we are to add it, I think we probably want:

TypeToken.java

  FluentIterable<Field> getAllDeclaredFields();
  FluentIterable<Invokable<T, ?>> getAllDeclaredMethods();

And leave it up to users to filter() on top of the returned list.

Although the bar for its inclusion is high because the TypeToken.getTypes() 
nested loop takes only one more line than the new API. It really needs to be 
used often to justify the new API.

Original comment by be...@google.com on 27 Feb 2013 at 3:17

GoogleCodeExporter commented 9 years ago
This issue has been migrated to GitHub.

It can be found at https://github.com/google/guava/issues/<id>

Original comment by cgdecker@google.com on 1 Nov 2014 at 4:13

GoogleCodeExporter commented 9 years ago

Original comment by cgdecker@google.com on 1 Nov 2014 at 4:18

GoogleCodeExporter commented 9 years ago

Original comment by cgdecker@google.com on 3 Nov 2014 at 9:08