Hi Barry,
> This is just an example I've pulled from the web but it seems to imply > that an Img<UnsignedByteType> is not safely considered an Img<RealType>. Good point! We should probably use "Img<? extends RealType>" instead. I tested this with ImageJ2, and it still works as expected. And I believe all of Steffi's example cases will still work too. So I pushed these changes to the newimgopener branch. What do you think, Steffi? Anyone else? Regards, Curtis On Tue, Jul 31, 2012 at 4:28 PM, Barry DeZonia <[email protected]> wrote: > Steffi, > > I remember running into this when originally interfacing IJ2 to Imglib. We > did decided to go with Img<? extends RealType<?> as that seemed the most > safe at the time. > > What I'm wondering about with your solution is that it seems to contradict > other people's description as to what is legal to do in Java. > > As a contrived example think of the animal cage example: > > We have a Animal class and a Lion class derived from it. And a Cage class > that is defined Cage<T extends Animal>. > > Though Lion is instanceof Animal its not the case that Cage<Lion> > instanceof Cage<Animal>. This is counterintuitive but many people mention > it. > > If it was the case you could write code like this: > > Cage<Animal> animals; > > Cage<Lion> lions; > > > animals = lions; // This assignment is not allowed > > > animals.add(rat); // If this executed, we'd have a Rat in a Cage<Lion> > > > This is just an example I've pulled from the web but it seems to imply > that an Img<UnsignedByteType> is not safely considered an Img<RealType>. We > may be avoiding all such cases where it could be a problem but is it safe > going forward? I can't say I know. > > > 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 >> > > -- > 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
