Hi Steffi, Thanks for the explanation, and sorry for the delay in reply; I have been on family leave for the past two weeks.
I really like your solution. In certain cases, I think raw types are the only way to get around the awkwardness of the recursive typing approach. I tested the newimgopener branch with ImageJ2, and it works. My vote is to merge to master. Regards, Curtis On Tue, Jul 17, 2012 at 4:08 PM, Stephan Preibisch <[email protected]>wrote: > Hi guys, > > this became a massive explanation. I suggest this as as solution to a > problem we have been only partly aware of. I hope you enjoy reading ;) > > Steffi > > > As the ImageJ conference is approaching I was talking with Johannes and we > agreed that the ImgOpener needs to be finished. However, since its first > version there has been a major design fault. > > But first the good news: > -------------------------------- > It works perfectly fine if you say "open me an Image as float" or "...as > UnsignedByte" or whatever, classically called by the method > -> new ImgOpener.openImg( String id, ImgFactory<T> factory, T type ); > It can, without any problems, return you an Img<T>, and it requires that T > is RealType (and not anymore NativeType, see at the end). So far, so good. > Note that "T" is NOT a return parameter, but something you give to the > openAs method. > > Now the bad news. > -------------------------- > If you say "open this image as whatever RealType it is", it can do that, > but it cannot assign a "T" to it - because T is not a return parameter. We > made an ugly hack inside so that it seems as if it would work, but it does > not. Now you might ask, why change it if it worked so far? Well, here is an > easy example that would cause a ClassCastException on run time > > public static <T extends RealType< T >> void main( String[] args ) > { > Img< T > img1 = new ImgOpener.openImg( "somepic_8bit.tif" ); // > 8-bit unsigned > Img< T > img2 = new ImgOpener.openImg( "somepic_32bit.tif" ); // > 32-bit float > > img1.firstElement().set( img2.firstElement() ); // run-time crash > } > > It will throw a ClassCastException because img1 is of UnsignedByteType and > img2 of FloatType. But as we cast it in a dirty way, it compiles just fine. > So, we cannot return a "T", but what we can return is Img< which is at > least a RealType >. And this is for sure always true, but img1 and img2 are > not necessarily the same RealType (as above). > > The correct way (which doesn't work) > -------------------------------------------------- > What we should return is an Img< ? extends RealType< ? > >. However, it is > not ensured that the two "?" are the same, so an Img of this type is > basically incompatible with everything else, which means an unchecked cast > is necessary. So although correct, maybe not a good idea. And it is really > ugly to write if necessary. > > The still somehow correct way > ----------------------------------------- > Instead, it returns now an Img< RealType >. This is kind of a tradeoff. On > one hand, this is very easy to write and will give you compile errors where > it should, for example > > img1.firstElement().set( img2.firstElement() ); // COMPILE ERROR > (not the same RealType realization) > > OR > > public <T extends RealType< T >> void add( IterableInterval< T > > i1, IterableInterval< T > i2 ) { .... } > add( img1, img2 ); // COMPILE ERROR (not the same RealType > realization) > > BUT > > Gauss.inFloatInPlace( 2.0 , img1 ); // FINE (just one RealType > realization required, inside it will be always the same "T") > Gauss.inFloatInPlace( 2.0 , img2 ); // FINE (just one RealType > realization required, inside it will be always the same "T") > > public < T extends RealType< T > > void add1( Img< T > img1, > double value ) { ... } > add1( img1, 5 ); // FINE (just one RealType realization required, > inside it will be always the same "T") > > public < T extends RealType< T >, S extends RealType< S > > void > add2( Img< T > img1, Img< S > img2 ) { ... } > add2( img1, img2 ); // FINE (explicitly two different RealType > realizations are allowed) > > public void add3( Img< RealType > img1, Img< RealType > img2 ) { > ... } > add3( img1, img2 ); // FINE (both are just some kind of RealType, > but gives a warning) > > On the other hand it gives a lot of Warnings because RealType should be > more specified. > > Why not Img< RealType< ? > > > ------------------------------------------ > Similar problem as in <? extends RealType< ? > >. RealType< ? > is not a > valid substitute for any construct like < T extends RealType < T > >. One > would have to cast to Img< RealType >, so we can take this one right away > as it is less writing. > > Where did NativeType go? > ------------------------------------ > I do not see any reason why we should enforce a NativeType. There is no > objection to load an image into a (hypothetical) ListImg< BigDecimalType >. > > What are the implications? > ----------------------------------- > We should write NOTHING except the method opening files for Img< RealType > >. And also only if it is really required - quite often it is not. But if, > Img< RealType > It is a completely valid input for any generic algorithm as > show above for Gauss, add, etc. > > > > I implemented the changes on a branch called 'newimgopener'. It also > contains four static convenience opening methods and a speed improvement. > > Any comments are very welcome. If somebody has a better idea how to solve > the problem ... I am all ears ... > > Bye bye, > Steffi > > -- > Please avoid top-posting, and please make sure to reply-to-all! > > Mailing list web interface: http://groups.google.com/group/fiji-devel >
_______________________________________________ ImageJ-devel mailing list [email protected] http://imagej.net/mailman/listinfo/imagej-devel
