Java Forum / General / December 2007
enums, using methods as initializers
Mikhail Teterin - 14 Nov 2007 19:54 GMT Hello!
I would like to be able to initialize fields of an enum with /methods/ (of another Class).
Here is the (non-working) example:
import java.util.*; import java.sql.*;
public enum Field { FIELD1 (ResultSet.getString), FIELD2 (ResultSet.getDouble), ... FIELDN (ResultSet.getTimestamp);
private java.lang.reflect.Method extract; }
the idea is to be able to get all fields from a given ResultSet by going through the list of Fields and extracting the column from the ResultSet.
Something like:
public void print(ResultSet rs) { for (Field f : Field.values()) System.out.println(f + ":\t" + rs.f.extract(f)); }
Does the above stand a chance of being turned into a real Java code?
Thanks for ideas!
-mi
Daniel Pitts - 14 Nov 2007 20:02 GMT > Hello! > [quoted text clipped - 31 lines] > > -mi Yes, kind of. You won't be able to pass in a method, but you can create a delegate method easily. I have an example here actually: <http://virtualinfinity.net/wordpress/program-design/2007/10/22/using-enums-as-a- flyweight-pattern/>
enums are full classes, so you can add methods to them, and even add an abstract method to the base enum type (Field in your case) and override those methods in the subtypes (FIELD1 FIELD2, etc...) .
 Signature Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
Mikhail Teterin - 14 Nov 2007 20:23 GMT > Yes, kind of. You won't be able to pass in a method, but you can create > a delegate method easily.
:(
> I have an example here actually: > <http://virtualinfinity.net/wordpress/program-design/2007/10/22/using-enums-as-a- flyweight-pattern/> In your example, each case of the enum spells-out its methods in full. It is workable, of course, but I wanted to have list the cases as a kind of a /table/ -- preferably one case per line.
I'm pretty certain, I can do this with my own /data/:
FIELD1 ("string"), FIELD2 ("double"), ... FIELDN ("Timestamp);
private String type;
and then use the type to tell me, which of method of the foreign class to call:
if (type == "string") return rs.getString(......) if (type == "double") return rs.getDouble(......) .... a case for each type ....
But it would all have been much easier, if I could pass the /methods/ the same way I can pass function-pointers in C or C++.
Thanks!
-mi
Daniel Pitts - 14 Nov 2007 20:59 GMT >> Yes, kind of. You won't be able to pass in a method, but you can create >> a delegate method easily. [quoted text clipped - 32 lines] > > -mi You can use reflection in this case, but thats not necessarily a good idea. Reflection can add unnecessary complexity.
I gather from your previous posts that you are used to programming in C/C++, and finding more concise manors to express a particular effect of code. Just remember, more lines doesn't mean more complex. In fact, /sometimes/ it means less complex :-)
Like I said, you *can* use reflection for this, but I advise against it: <http://virtualinfinity.net/wordpress/program-design/2007/01/11/the-dangers-of-re flection-or-put-down-that-mirror/>
What you're trying to do sounds a lot like something that I did a while ago. It also sounds like you would be better off using Hibernate or some other ORM solution. Trust me, the ramp-up time is well-worth the maintenance costs down the road.
 Signature Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
Mikhail Teterin - 15 Nov 2007 02:58 GMT > You can use reflection in this case, but thats not necessarily a good > idea. Reflection can add unnecessary complexity. Maybe, one can just treat all data-fields of a class as a Collection or some such?
class Meow { String foo; double bar; }
....
for (Field field : Meow.fields()) { if (field.getType() == String) .....
?
> I gather from your previous posts that you are used to programming in > C/C++, and finding more concise manors to express a particular effect of > code. Just remember, more lines doesn't mean more complex. In fact, > sometimes it means less complex :-) The idea here is that the concise declarative part can be maintained by someone else as "data", while I maintain the code...
The shorter the program, the fewer screenfuls it takes, the fewer bugs :)
Thanks!
-mi
Daniel Pitts - 15 Nov 2007 16:35 GMT > The idea here is that the concise declarative part can be maintained by > someone else as "data", while I maintain the code... > > The shorter the program, the fewer screenfuls it takes, the fewer bugs :) Not necessarily. Less verbosity *can* lead to fewer bugs, but it can also be too terse to understand. In any case, I think that this situation *is* ripe for a better solution... I've suggested it before. Hibernate! Google for it. Use it. You no longer have to deal with low-level JDBC stuff, it'll help manage your schema for you if you choose, and the CRUD operations are super easy.
> Thanks! No problem. If you choose *not* to use Hibernate, a response of why would be appreciated.
Daniel.
 Signature Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
Lew - 16 Nov 2007 06:11 GMT > Hibernate! Google for it. Use it. You no longer have to deal with > low-level JDBC stuff, it'll help manage your schema for you if you > choose, and the CRUD operations are super easy.
> ... If you choose *not* to use Hibernate, a response of why > would be appreciated. I'm learning Hibernate, and the JPA annotations generally. It's a bit tricky learning to configure the DataSource, but I'm slogging through it.
I've written whole entire data-access layers, the most recent replete with generic <Entity> typing and all kinds of nifty transaction boundaries and exception logging - whew. I am really hoping the Hibernatic approach will make life easier. It may seem easy to do database, but to do it right, with rigor and reliability takes a lot.
Having labored through the "by-hand" approach, I really get where the annotations are coming from.
The devil is in the deployment.
 Signature Lew
mekane - 15 Nov 2007 19:36 GMT > Hello! > [quoted text clipped - 31 lines] > > -mi I assume you've looked at: http://java.sun.com/j2se/1.5.0/docs/guide/language/enums.html
I had a case where I needed to do something similar to this, and I tried overriding a method on each element of the enum. I didn't like defining big methods inside the constructor, so I actually went with the method described in the article that uses a switch.
So you would have one method defined in the enum like:
public String extract( Resultset arg ){ switch ( this ) { case FIELD1: return arg.getString(); case FIELD2: return arg.getDouble(); ... case FIELDN: return resultSet.getTimestamp(); } }
then you could iterate over the values of the enum and do f.extract(rs)
-marty
Daniel Pitts - 15 Nov 2007 21:17 GMT >> Hello! >> [quoted text clipped - 59 lines] > > -marty That is very specifically a Bad Idea! f.extract should NOT have a switch statement, but instead should be polymorphic.
 Signature Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
mekane - 15 Nov 2007 21:29 GMT > That is very specifically a Bad Idea! > f.extract should NOT have a switch statement, but instead should be > polymorphic. What difference does it make? Anything other than I might forget to add another case? Is this more than just an implementation detail?
Daniel Pitts - 15 Nov 2007 21:35 GMT >> That is very specifically a Bad Idea! >> f.extract should NOT have a switch statement, but instead should be [quoted text clipped - 3 lines] > another case? > Is this more than just an implementation detail? Yes, it is far more than an implementation detail, it is a design principal.
Switch statements should be avoided. I know this is going to sound snobby, but polymorphic behavior is far superior for this situation. Not only is it likely to have better performance, it is easier to refactor into a more useful idiom. Say someone wants to add a custom field extractor, its easy to change these enums into a regular class, and have the extract be a method in an interface. That way, the client can say "extract this field with this approach."
 Signature Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
mekane - 15 Nov 2007 21:47 GMT >>> That is very specifically a Bad Idea! >>> f.extract should NOT have a switch statement, but instead should be [quoted text clipped - 13 lines] > the extract be a method in an interface. That way, the client can say > "extract this field with this approach." I see. That makes sense, and I would agree that polymorphism is much more elegant. But isn't the point of an enum to say "here are all the possible values of this type, that's it". So a better design decision here would be to use something other than enums in the first place. Especially if you can't say for sure that the fields will never change.
To me, an enum and a switch work nicely together, especially when the alternative is to write different versions of a big, complicated method in the definition of an enum.
I'm not trying to argue, I'm just expressing an opinion.
Would you never use a switch?
Daniel Pitts - 15 Nov 2007 22:14 GMT >>>> That is very specifically a Bad Idea! >>>> f.extract should NOT have a switch statement, but instead should be [quoted text clipped - 27 lines] > > Would you never use a switch? I very much try to avoid switch (or if/elseif/elesif,etc..) as much as feasible.
Switch is a remnant of procedural programming languages. Often times it was used to create polymorphic behavior based on a "type" token. Well, now you have a "type" that can do that polymorphic behavior for you.
I'm not saying there are NEVER times when you can use switch statements, I'm just saying that by the time I need one switch statement, I probably need two, and at that point its time to use polymorphism and create an abstract method for each of my switch statements. As a mater of fact, I would *love* a tool that could take an switch(enum) and convert it to enum.method().
Hear that JetBrains? Make it happen :-)
 Signature Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
Lew - 16 Nov 2007 06:56 GMT > I very much try to avoid switch (or if/elseif/elesif,etc..) as much as > feasible. [quoted text clipped - 11 lines] > > Hear that JetBrains? Make it happen :-) One major advantage of the polymorphic approach is that you get compiler enforcement. You cannot "fall" into an unexpected case, or forget to implement a behavior.
In fact, I find the quirky combination of the class attitude and the enum constant ancestry along with the peculiarities of Java's implementation as a pseudo-inherited Enum class with occasional implicit inner classes extending the enum to be a strangely, emergently powerful mechanism. For one thing, enums may hold the power to release us from the temptation to reflection.
Back to the OP's question, Daniel, were you thinking of something like this, only maybe better refactored?
You'd use it something like:
Object val = Noom.valueOf( rsMetaData.getColumnType( col ) ) .getValue( rs, col );
(throws NPE)
<sscce> public enum Noom { BOOLEAN( Types.BOOLEAN ) { @Override public Boolean getValue( ResultSet rs, int column ) { try { return (rs.getObject( column ) == null? null : Boolean.valueOf( rs.getBoolean( column ))); } catch ( SQLException ex ) { logger.error( "SQL Exception"+ ex.getMessage(), ex ); return null; } }
} , VARCHAR( Types.VARCHAR ) { @Override public String getValue( ResultSet rs, int column ) { try { return rs.getString( column ); } catch ( SQLException ex ) { logger.error( "SQL Exception"+ ex.getMessage(), ex ); return null; } }
} ; private final int sqlType; private Noom( int sqlT ) { this.sqlType = sqlT; }
public static Noom valueOf( int sqlT ) { for ( Noom noom : values() ) { if ( noom.sqlType == sqlT ) { return noom; } } return null; }
private static final Logger logger = Logger.getLogger( Noom.class );
public abstract Object getValue( ResultSet rs, int column ); } </sscce>
 Signature Lew
Jeff Higgins - 16 Nov 2007 08:38 GMT > <sscce> ...
> </sscce> ? Lew - 16 Nov 2007 14:53 GMT >> <sscce> > .... >> </sscce> ? <http://www.physci.org/codes/sscce.html>
GIYF.
 Signature Lew
mekane - 16 Nov 2007 16:53 GMT > One major advantage of the polymorphic approach is that you get compiler > enforcement. You cannot "fall" into an unexpected case, or forget to > implement a behavior. Agreed. I like compiler enforcement.
> </sscce> > snip > </sscce> That example from Lew may have just completely swayed me. I figured that writing methods inside enum definitions would be hideous, but that example was quite nice looking. I'm going to refactor a hobby project I'm working on and see how it goes. I was originally going to use the polymorphic approach, but I went with a switch instead.
Thanks for the interesting discussion.
-marty
Roedy Green - 15 Nov 2007 23:56 GMT On Wed, 14 Nov 2007 14:54:35 -0500, Mikhail Teterin <usenet+mill@aldan.algebra.com> wrote, quoted or indirectly quoted someone who said :
>I would like to be able to initialize fields of an enum with /methods/ (of >another Class). For background read the various example bits of code at http://mindprod.com/jgloss/enum.html
Once you understand your building blocks, I think you will be off and running.
 Signature Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Mark Space - 16 Nov 2007 20:10 GMT > Hello! > [quoted text clipped - 5 lines] > import java.util.*; > import java.sql.*; The important bit for me is that last line. SQL? Doesn't SQL already have some methods for dealing with tabular data? Trying to shoe-horn this into an enum seems like a bad idea. Use what's there already, it's likely to be far more useful and flexible in the long run. Enums are likely to box you into a corner.
Mikhail Teterin - 21 Nov 2007 01:47 GMT > The important bit for me is that last line. SQL? Doesn't SQL already > have some methods for dealing with tabular data? It does -- there are getString(), getInt(), getDouble(), etc.
The problem I'm facing is that my rows return A LOT of columns, which must all be tediously assigned to fields of a class. This is, roughly, what I do in the constructor (each row creates an object of type Entry):
public class Entry { public String foo; public double bar; ... public Date woof;
public Entry(ResultSet rs) { foo = rs.getString("foo"); bar = rs.getDouble("bar"); ... woof = rs.getTimestamp("woof"); } }
What I'm looking for is a way to go through all fields and extract them from the row in a loop. Something like:
public Entry(ResultSet rs) { for (WHAT? field : FieldsOfEntry?) { field = rs.MethodForField(field.toString());
} }
This would allow me to add/remove fields without changing the code every time. Sort of make it "data-driven" with the fields of the class themselves being the "data".
I'll look up "Hibernate", but I was hoping, a solution can be found, since Java (unlike C) keeps the fields' names and types around at run-time anyway...
Thanks!
-mi
Stefan Ram - 21 Nov 2007 02:19 GMT >What I'm looking for is a way to go through all fields and >extract them from the row in a loop. { final java.sql.ResultSetMetaData desc = resultSet.getMetaData(); final int cols = desc.getColumnCount(); for( int i = 1; i <= cols; ++i ) java.lang.System.out.println ( desc.getColumnName( i )+ " " + resultSet.getString( i )); }
I have not tested the above code, so it still might contain bugs. You also can get the type from the meta data. See
http://download.java.net/jdk7/docs/api/java/sql/ResultSetMetaData.html
Mikhail Teterin - 21 Nov 2007 16:47 GMT >>What I'm looking for is a way to go through all fields and >>extract them from the row in a loop. [quoted text clipped - 9 lines] > > http://download.java.net/jdk7/docs/api/java/sql/ResultSetMetaData.html Thank you, that's pretty cool. But that goes through the columns of the ResultSet. I'm trying to go through the fields of my own Class, however.
I know, I can store the data in my own HashTable, but I would rather access the fields as entry.foo and entry.meow instead of entry.getFoo() and entry.getMeow().
Thanks!
-mi
Daniel Pitts - 21 Nov 2007 19:19 GMT >>> What I'm looking for is a way to go through all fields and >>> extract them from the row in a loop. [quoted text clipped - 19 lines] > > -mi If you REALLY REALLY want to go through the fields of a class (Bad Idea), you can use reflection.
Reflection is difficult to get right, so I suggest delegating that responsibility to a library that is maintained by a large community. Hibernate does exactly what you want. *Exactly* what you want. Let me repeat. Hibernate does *exactly* what you're trying to do.
If you insist on going the route of DIY, read my warning about reflection:
<http://virtualinfinity.net/wordpress/program-design/2007/01/11/the-dangers-of-re flection-or-put-down-that-mirror/>
If after reading that, you feel justified in using reflection, the sun tutorial on reflection is a good starting place: <http://java.sun.com/docs/books/tutorial/reflect/index.html>
Just know that I've gone down the road you're trying to. It isn't a pretty journey, and the destination isn't all that nice either.
 Signature Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
Mikhail Teterin - 12 Dec 2007 22:58 GMT > If you REALLY REALLY want to go through the fields of a class (Bad > Idea) Uhm, why? I'm quite sure, we are not alone here with the need to have an object for every row return by an SQL-query...
> you can use reflection. Well, here is what I cooked up:
http://aldan.algebra.com/~mi/selfsetting/SelfSettingFromSQL.java.html or http://aldan.algebra.com/~mi/selfsetting/SelfSettingFromSQL.java
It will set the scalar fields and even arrays. The same java.sql.ResultSet can be used to create (or set) different objects -- the columns without matching fields in each object's class will simply be ignored.
A typical use would be to define your own class as "extends SelfSettingFromSQL".
> Hibernate does exactly what you want. Exactly what you want. Let me > repeat. Hibernate does exactly what you're trying to do. NOW, I can go and look into how someone else has done it :)
> Just know that I've gone down the road you're trying to. It isn't a > pretty journey, and the destination isn't all that nice either. So far I like it :) All I need now is declare the data-fields in my classes and make sure, the SQL-queries return fields with matching names. The tedious setting of every field (in every one of those classes) is now a thing of the past.
The information about all the names and the types of all the fields is there at run-time. Not using it is foolish...
-mi
Mikhail Teterin - 12 Dec 2007 23:07 GMT > If you REALLY REALLY want to go through the fields of a class (Bad > Idea) Uhm, why? I'm quite sure, we are not alone here with the need to have an object for every row return by an SQL-query...
> you can use reflection. Well, here is what I cooked up:
http://aldan.algebra.com/~mi/selfsetting/SelfSettingFromSQL.java.html or http://aldan.algebra.com/~mi/selfsetting/SelfSettingFromSQL.java
It will set the scalar fields and even arrays. The same java.sql.ResultSet can be used to create (or set) different objects -- the columns without matching fields in each object's class will simply be ignored.
A typical use would be to define your own class as "extends SelfSettingFromSQL".
> Hibernate does exactly what you want. Exactly what you want. Let me > repeat. Hibernate does exactly what you're trying to do. NOW, I can go and look into how someone else has done it :)
> Just know that I've gone down the road you're trying to. It isn't a > pretty journey, and the destination isn't all that nice either. So far I like it :) All I need now is declare the data-fields in my classes and make sure, the SQL-queries return fields with matching names. The tedious setting of every field (in every one of those classes) is now a thing of the past.
The information about all the names and the types of all the fields is there at run-time. Not using it is foolish...
-mi
Stefan Ram - 21 Nov 2007 22:01 GMT >Thank you, that's pretty cool. But that goes through the columns of the >ResultSet. I'm trying to go through the fields of my own Class, however. public class Main { public static void main( final java.lang.String[] args ) throws java.lang.Exception { class Example { int i; java.lang.String s; }; for( java.lang.reflect.Field field: Example.class.getDeclaredFields() ) java.lang.System.out.println( field.getName() ); }}
i s
Roedy Green - 21 Nov 2007 11:39 GMT On Wed, 14 Nov 2007 14:54:35 -0500, Mikhail Teterin <usenet+mill@aldan.algebra.com> wrote, quoted or indirectly quoted someone who said :
>FIELD1 (ResultSet.getString), You will have to pass a value here. e.g. OtherClass.resultSet.getString()
You seem to be trying to pass a method as av argument. You can't do that in Java. See http://mindprod.com/jgloss/callback.html
 Signature Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Wayne - 21 Nov 2007 18:30 GMT > On Wed, 14 Nov 2007 14:54:35 -0500, Mikhail Teterin > <usenet+mill@aldan.algebra.com> wrote, quoted or indirectly quoted [quoted text clipped - 7 lines] > You seem to be trying to pass a method as av argument. You can't do > that in Java. See http://mindprod.com/jgloss/callback.html Of course you can create a Method object and pass that, and then invoke it, but I don't think that's what the OP had in mind.
-Wayne
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|