Community
Participate
Working Groups
For example the input parameter type of setContentProvider(IContentProvider<I>) should be IContentProvider<? extends I> to allow clients to set subtypes of IContentProvider<I> as ContentProvider. This would allow a more flexible use for the client.
The usage of extends is not that useful in this case. An upper bounded wildcard would allow to set a more specific ContentProvider, which could possibly not handle the input of the type I. For example we change the signature to setContentProvider(IContentProvider<? extends I> cp). Now create a Viewer which handles every Object as input TableViewer<XYZ,Object>. With the upper bounded wildcard, it is now possible so set a ContentProvider which handles a subtype of Object. tableViewer.setContentProvider(new IContentProvider<String>(){}); This is wrong, because now we can set an Object as input which can not be handled my the assigned "String ContentProvider" So to be less restrictive we should use lower bounded wildcards. With the Signature setContentProvider(IContentProvider<? super I> cp) it is possible to assign ContentProvider which can handle input from the type I or its super types. For example with the viewer TableViewer<String,MyDAO>() it is now possible to set a ContentProvider which handles input of every type. tableViewer.setContentProvider(new IContentProvider<Object>(){});
Created attachment 235148 [details] Example for incorrect generics implementation I agree with Hendriks analysis. If we could use void setContentProvider(IContentProvider<? extends I> provider) on StructuredViewer we could not avoid runtime failures. I attach an example for such an incorrect behavior. setContentProvider(IContentProvider<? super I> provider) avoids IMHO CCE.
Adding Ed as he suggested <? extends I> for the content provider. @Ed we think <? super I> is the correct implementation.
Yes, I see what you mean! It's more like java.util.Collections.sort(List<T>, Comparator<? super T>) where you have to ensure that the comparator can properly receive any of the list's elements...
Currently it is possible to use the <? super I> type only with a casting. My idea was to also use the super-type not only as input type for the setContentProvider method, but also as type for the field contentProvider in the ContentViewer. But this leads to compiler error, which I don't really understand. If the contentProvider field is typed as <? super I> the following line will not compile: contentProvider.inputChanged(this, getInput(), null); With the following error message: The method inputChanged(Viewer<capture#3-of ? super I>, capture#3-of ? super I, capture#3-of ? super I) in the type IContentProvider<capture#3-of ? super I> is not applicable for the arguments (ContentViewer<E,I>, I, null) I tracked the error down with the sample class I'll attache.
Created attachment 235293 [details] Sample Class to describe the compile error
(In reply to Hendrik Still from comment #5) > Currently it is possible to use the <? super I> type only with a casting. > My idea was to also use the super-type not only as input type for the > setContentProvider method, but also as type for the field contentProvider in > the ContentViewer. But this leads to compiler error, which I don't really > understand. The problem is your provider interface: public class Provider<I> { void doSomeThing(List<I> inData){} } It expects exactly a list that promises to accept elements of type I and to return elements of type I. In other words, the compiler wants to ensure that implementations of that interface may read and write the the 'inData' without type propblems. Now consider that the type variable I is bound to '? super T' which is not concrete but only an existential type, e.g. the compiler will only ensure that this is some type which exists and which is a super type of T. Based on that, it cannot be proven that the List of T is not used in an invalid manner. To give you an example on what happens if you use a cast: class ProviderContainer<T>{ Provider<? super T> provider; public void setProvider(Provider<? super T> provider) { this.provider = provider; } void doSomeThingWithTheProvider(List<T> inData){ // cast here ((Provider<T>)this.provider).doSomeThing(inData); } public static class Provider<I> { void doSomeThing(List<I> inData){ System.out.println(inData); } } } // custom provider implementation that sticks to the // contract of Provider<T> class MyProvider extends ProviderContainer.Provider<CharSequence> { @Override void doSomeThing(List<CharSequence> inData) { // we add a StringBuilder to the list of char sequences inData.add(0, new StringBuilder()); } } class MyProviderContainer extends ProviderContainer<String> { @Override void doSomeThingWithTheProvider(List<String> inData) { super.doSomeThingWithTheProvider(inData); // caboom in the next line // since we read from the list as String but the provider // legally added a string builder String first = inData.get(0); } public static void main(String[] args) { MyProviderContainer container = new MyProviderContainer(); container.setProvider(new MyProvider()); container.doSomeThingWithTheProvider(new ArrayList<String>()); } } A better way to solve the compile problem is to think about the contract of a provider, e.g. it could be worthwhile to only read from the the list and thereby the API should only guarantee that readers will get some instance of I and disallowing writer to alter the list, e.g to doSomething(List<? extend I> inData) is compilable and will prevent CCE due to valid client code.
Thank you Sebastian for your really quick answer. I had to think more than twice about it. Thank you very much.
I have merged Hendrik's fix into the generics branch so we can try it out on real world examples: http://git.eclipse.org/c/platform/eclipse.platform.ui.git/log/?h=johna/402445
There was a small additional fix to some synchronization code in Hendrik's previous fix: http://git.eclipse.org/c/platform/eclipse.platform.ui.git/commit/?h=johna/402445&id=4821302fabf4f316b32d55acd083484863a9ce3f
Reopen for investigation for migration to master for Neon.
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet. As such, we're closing this bug. If you have further information on the current state of the bug, please add it and reopen this bug. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant. -- The automated Eclipse Genie.