Possibly a question which has been asked before, but as usual the second you mention the word generic you get a thousand answers explaining type erasure. I went through that phase long ago and know now a lot about generics and their use, but this situation is a slightly more subtle one.

I have a container representing a cell of data in an spreadsheet, which actually stores the data in two formats: as a string for display, but also in another format, dependent on the data (stored as object). The cell also holds a transformer which converts between the type, and also does validity checks for type (e.g. an IntegerTransformer checks if the string is a valid integer, and if it is returns an Integer to store and vice versa).

The cell itself is not typed as I want to be able to change the format (e.g. change the secondary format to float instead of integer, or to raw string) without having to rebuild the cell object with a new type. a previous attempt did use generic types but unable to change the type once defined the coding got very bulky with a lot of reflection.

The question is: how do I get the data out of my Cell in a typed way? I experimented and found that using a generic type could be done with a method even though no constraint was defined

The bit that puts me off is: that is ? it can't be casting anything, there is no input of type T which constrains it to match anything, actually it doesn't really say anything anywhere. Having a return type of Object does nothing but give casting complications everywhere.

In my test I set a Double value, it stored the Double (as an object), and when i did Double testdou = testCell.getValVal(); it instantly worked, without even an unchecked cast warning. however, when i did String teststr = testCell.getValVal() I got a ClassCastException. Unsurprising really.

There are two views I see on this:

One: using an undefined Cast to seems to be nothing more than a bodge way to put the cast inside the method rather than outside after it returns. It is very neat from a user point of view, but the user of the method has to worry about using the right calls: all this is doing is hiding complex warnings and checks until runtime, but seems to work.

The second view is: I don't like this code: it isn't clean, it isn't the sort of code quality I normaly pride myself in writing. Code should be correct, not just working. Errors should be caught and handled, and anticipated, interfaces should be foolproof even if the only expecter user is myself, and I always prefer a flexible generic and reusable technique to an awkward one off. The problem is: is there any normal way to do this? Is this a sneaky way to achieve the typeless, all accepting ArrayList which returns whatever you want without casting? or is there something I'm missing here. Something tells me I shouldn't trust this code!

perhaps more of a philosophical question than I intended but I guess that's what I'm asking.

When assigning a double to typedElem and trying to put it into a String I get an exception NOT on the cast to , but on the return, and the second snippet does not protect. The output from the getClass is java.lang.Double, suggesting that is being dynamically inferred from typedElem, but that compiler level type checks are just forced out of the way.

As a note for the debate: there is also a function for getting the valClass, meaning it's possible to do an assignability check at runtime.

Edit2: result

After thinking about the options I've gone with two solutions: one the lightweight solution, but annotated the function as @depreciated, and second the solution where you pass it the class you want to try to cast it as. this way it's a choice depending on the situation.

One more comment on this post (besides everything I wrote below): I think in this case the extra compiler checking you are trying to achieve won't actually catch many bugs. If you make your code as concise as possible (although you forego some type checking), you'll probably wind up with a more reliable program in the end.
–
Alex DSep 24 '12 at 18:29

Library writing is always that line between functionality and simplicity. I am thinking about either taking it as it is (you shouldn't read data without knowing what it is, and the transformers let you check anyway) or Dirk's proposal (just give it an input and take the type off that).
–
K.BaradSep 24 '12 at 18:43

Note, that this does not do any conversion (i.e., you cannot use this method to extract a Double, if valVal is an instance of Float or Integer).

You should get, btw., a compiler warning about your definition of getValVal. This is, because the cast cannot be checked at run-time (Java generics work by "erasure", which essentially means, that the generic type parameters are forgotten after compilation), so the generated code is more like:

I know how erasure works yes, although the generated code is a little more complex than that. I've pondered a method like that already, but isInstance is not a very good check, I am more after Class.isAssignableFrom(cls). It's not as elegant as I'd like it but it is one of the ways I can get the assertion in place. I could always have both options, with a disclaimer. It would be easier to use the simple option for simple situations with a predictable datatype, and then the stronger option for the truly dynamic case. It could be made easier to not ask for the class, but just an example
–
K.BaradSep 24 '12 at 18:14

The more I think about it the more this seems like the safest solution. I'm sure I can find a way to deal with the extra complexity. The more convincing point is that this is the method used for a lot of the other collections libraries, suggesting there isn't a cleaner or nicer method. If you want to be able to enforce the type you need to get it from somewhere.
–
K.BaradSep 24 '12 at 18:27

From my experience, even though the JVM doesn't do a check at the point of source-explicit cast, it will still do a check when you try assigning the result of the generified method to a variable of the expected type (and I actually find that to be a bit better in most cases than having the exception being thrown within the getValue() method, as in cases like that it's normally an issue with the code at the point of method call rather than anything in the getValue() method itself, so having the topmost item in the stacktrace being the line where the method is called makes more sense to me).
–
JABJun 7 '13 at 14:39

(Well, assuming the type parameter is not itself a generified type, of course. Things get a lot trickier when you're doing casts with generified types. I also thought there might be issues with assigning objects to a variable when the actual object is an instance of a subclass of the variable's type, but when I double-checked it seems that it's perfectly fine to cast a variable to a superclass or implemented interface, as getClass() and instanceof still work with the actual class of the value.)
–
JABJun 7 '13 at 15:04

As you are discovering, there is a limit to what can be expressed using Java's type system, even with generics. Sometimes there are relationships between the types of certain values which you would like to assert using type declarations, but you can't (or perhaps you can, at the cost of excess complexity and long, verbose code). I think the sample code in this post (question and answers) is a good illustration of that.

In this case, the Java compiler could do more type checking if you stored the object/string representation inside the "transformer". (Perhaps you'll have to rethink what it is: maybe it's not just a "transformer".) Put a generic bound on your base Transformer class, and make that same bound the type of the "object".

As far as getting the value out of the cell, there's no way that compiler type checking will help you there, since the value can be of different types (and you don't know at compile time what type of object will be stored in a given cell).

I believe you could also do something similar to:

public <T> void setObject(Transformer<T> transformer, T object) {}

If the only way to set the transformer and object is through that method, compiler type checking on the arguments will prevent an incompatible transformer/object pair from going into a cell.

If I understand what you're doing, the type of Transformer which you use is determined solely by the type of object which the cell is holding, is that right? If so, rather than setting the transformer/object together, I would provide a setter for the object only, and do a hash lookup to find the appropriate transformer (using the object type as key). The hash lookup could be done every time the value is set, or when it is converted to a String. Either way would work.

This would naturally make it impossible for the wrong type of Transformer to be passed in.

I know there are limitations and I'm close to the edge. That's why I'm trying to get a feel for which side of the edge I'm on. I don't think I'm too upset if I can't get an active assertion that this is a safe cast, I know better than to expect this is standard usage and generics have generally been a great tool and very useful in the past for me. the Transformer is an interface, and not so simple as the example of course. A DateTransformer concept would be an implementation that expects a certain date format, you could have a PercentageTransformer, MonthTransformer, or custom types.
–
K.BaradSep 24 '12 at 17:53

If it's an interface, you can still parameterize the interface, and give it getters/setters which use the generic bound of the interface itself, right? ie interface Transformer<T> { T get(); void set(T object); }
–
Alex DSep 24 '12 at 17:57

The Transformer (and some of the other functions left out of the example) is part of a library I'm rebuilding (since the previous typed version was clearly too cumbersome). In particular my transformer interface is double typed, and my cell locks it down as <String, ?>. I have quite a few functions I'm building up as a concept of DualTyped data which exists in two forms, with the Transformers forming the exchange and checks. The Cell, however, isn't so constrained. As far as I can get it there is no way to make a general type variable which is dynamic but shared, correct me if I'm wrong?
–
K.BaradSep 24 '12 at 17:59

sorry, comment took more than 1 box. I think we are commenting in parallel :P. The transformer is typed, but that is another file. The issue is that type parameter is only applicable inside a transformer, and more specifically it is fixed the instant you instantiate it. But a cell may have it's value and transformer removed and replaced with a different transformer and recalculated. This is why I'm trying to take complexity out of the cell and the spreadsheet to make it something that only applied to the data.
–
K.BaradSep 24 '12 at 18:03

1

I also think that putting the value inside the transformer doesn't really make sense -- it was just a way to get stricter type checking out of the compiler. I just added another idea for you...
–
Alex DSep 24 '12 at 18:05

As much as I like this answer, it cheating to say, "Use a different language where you don't have this problem."..
–
dcowSep 24 '12 at 17:48

not going outside of standard Java. Four reasons are compatiblity and portability, availabilty of libraries on other machines, collaboration with others, and I have a mental dislike of huge libraries/language extensions which are more complex to use than it is to make it yourself.
–
K.BaradSep 24 '12 at 17:49

@DavidCowden -- it's not cheating. Groovy, Scala, Clojure, and JRuby all run on the JVM and can interoperate with the OP's Java codebase. More than that, the software industry as a whole needs to get the message that we have much better languages than Java/C#/C++ now. Java was fine back in 1995, but we can do much, much better now, and people need to keep hearing that.
–
Alex DSep 24 '12 at 17:52

1

@K.Barad -- the language which WillP is showing you here is compatible with Java, and is portable to anywhere where Java goes (because it runs on the JVM). It can call into Java code, so availability of libraries is not a problem. As for collaboration with others, many people are using these newer languages already, and more will follow. If you follow WillP's advice and learn a newer, Java-compatible language, you will not only solve the problem you have with generics -- you may find it revolutionizes your whole way of programming.
–
Alex DSep 24 '12 at 17:55