Based on the code snippet listed in my previous question (copied from a Java tutorial on generics by Gilad Bracha), I wrote the following code which tries to populate a custom generic list called history with objects of different types conforming to the bound ? extends Shape. However, my program doesn't compile. Why? How is it different from the code snippet in my other question?
import java.util.ArrayList;
class Shape {}
class Circle extends Shape {}
class Rectangle extends Shape {}
class MyArrayList<T>
{
private ArrayList<T> items = new ArrayList<>();
public T get(int idx)
{
return items.get(idx);
}
public void add(T item)
{
items.add(item);
}
#Override
public String toString()
{
String str = "A box of [";
for (T item : items) { str += item; }
str += ']';
return str;
}
}
public class Shapes
{
private static MyArrayList<? extends Shape>
history = new MyArrayList<>();
public static void main(String[] args)
{
history.add(new Circle());
history.add(new Rectangle());
System.out.println(history);
}
}
In your previous example, you had a List<List<? extends Shape>>. Here you have a List<? extends Shape>. Completely different things.
Given a MyArrayList<? extends Shape>, that is a REFERENCE (like a page in an address book, not like a house), and the 'page in the address book' (that'd be your variable) promises that the house you get to will be guaranteed to be one of a few different style. Specifically, it'll be a List<Shape>. Or it could be a List<Rectangle>. Or perhaps a List<Circle>. But it'll be either a list of all sorts of shapes, or a list of some sort of specific shape.
This is different from just 'it's a list of all sorts of shapes' - that would be a List<Shape>, not a List<? extends Shape>.
Given that it could be a list of circles, and it could also be a list of squares, everything you do with history needs to be a thing you could do to either one.
.add(new Circle()) is not something that is valid for a List<Rectangle>. Hence, you can't do that, and history.add(new Circle()) is a compilation error.
So why did the previous snippet work? Because that was about List<List<? extends Shape>> which is a completely different thing to a List<? extends Shape>. One stores shapes. One stores a list of shapes.
Related
This isn't a JavaFX question, but I'm trying to write an interface in JavaFX that declares a class to be Viewable. Viewable classes are meant to have a view() method that returns a Node object representing that Viewable. Simple so far, but here's where it gets complicated. The returned Node should be guaranteed to have a getViewable() method that returns the Viewable object it represents. How do I accomplish this? My first instinct was to try something like this:
interface Viewable<V extends Viewable<V>>{
<N extends Node&View<V>>N view();
}
interface View<V extends Viewable<V>>{
V getViewable();
}
Which at first appears sound, and allows classes like the following:
class ViewableObject implements Viewable<ViewableObject>{
#Override public ObjectView view(){
return new ObjectView();
}
class ObjectView extends Pane implements View<ViewableObject>{
#Override public ViewableObject getViewable(){
return ViewableObject.this;
}
}
}
However, for some reason, this class also compiles:
class ViewableObject implements Viewable<ViewableObject>{
#Override public Pane view(){
return new Pane();
}
}
Pane is a Node, but it does not implement View, so why does this class compile? I would think this is violating the contract of the view() method. Even stranger, the same class fails to compile when Pane is replaced with Object:
class ViewableObject implements Viewable<ViewableObject>{
#Override public Object view(){//complains this is not an #Override
return new Object();
}
}
What's going on here? Is there a flaw in my understanding of generics? How can I get this to work as intended?
You don't want to use a generic method in this case, since your goal is to fix the type of view()'s return value. A generic method lets the caller determine the concrete types. So really you're doing the exact opposite of enforcing.
I think you'd want to build the type parameter for the Node's type into the interface definitions, which will enforce that view() returns the correct type. Maybe something like this:
interface Viewable<V extends Viewable<V, N>, N extends Node & View<V, N>> {
N view();
}
interface View<V extends Viewable<V, TNode>, TNode extends Node & View<V, TNode>> {
V getViewable();
}
class ViewableObject implements Viewable<ViewableObject, ViewableObject.ObjectView> {
#Override
public ObjectView view() {
return new ObjectView();
}
class ObjectView extends Pane implements View<ViewableObject, ObjectView> {
#Override
public ViewableObject getViewable() {
return ViewableObject.this;
}
}
}
If you take a look at the byte code for the declaration of Viewable.view(), you'll see that the compiler selects the first type bound to specify as the actual return type for the method. Here are the relevant lines of the output from the IntelliJ byte code viewer:
// declaration: N view<N extends org.cumberlw.viewtest.Node, org.cumberlw.viewtest.View<V>>()
public abstract view()Lorg/cumberlw/viewtest/Node;
So when overriding you can specify any type that is covariant with the first type only and the compiler will accept it. If you switch the order of the type bounds, this is what you'll see in the byte code viewer:
// declaration: N view<N extends org.cumberlw.viewtest.View<V>, org.cumberlw.viewtest.Node>()
public abstract view()Lorg/cumberlw/viewtest/View;
Notice that the byte code says the return value is View now. So now your second example won't compile because Pane is not a subclass of View. Neither order of the parameters will let the third example compile because Object is not a subclass of Node or View.
Overriding a method with a generic return type with multiple bounds can easily produce runtime errors too. The compiler only enforces that return types be covariant with the first type bound, so you can return a type that does not conform to the second type bound. For example this compiles fine, but crashes at runtime:
interface DogLike {
void bark();
}
interface CatLike {
void meow();
}
class Dog implements DogLike {
#Override
public void bark() {
System.out.println("Woof");
}
}
interface MoreauMachine {
<H extends DogLike & CatLike > H createHybrid();
}
class MalfunctioningDogCatFactory implements MoreauMachine {
#Override
public DogLike createHybrid() {
//Compile with -Xlint:unchecked to see a warning here:
//Warning:(84, 20) java: createHybrid() in org.cumberlw.viewtest.MalfunctioningDogCatFactory implements <H>createHybrid() in org.cumberlw.viewtest.MoreauMachine
//return type requires unchecked conversion from org.cumberlw.viewtest.DogLike to H
return new Dog();
}
public static void main(String[] args) {
MoreauMachine factory = new MalfunctioningDogCatFactory();
//crashes!
//Exception in thread "main" java.lang.ClassCastException: org.cumberlw.viewtest.Dog cannot be cast to org.cumberlw.viewtest.CatLike
factory.createHybrid().meow();
}
}
I'm studying generics in Java, and was getting along comfortably until I reached the topic- Creating a generic method.
I know that in Java, generics are used when you want to implement something irrespective of the data type that the program(or method) operates upon. So, you could have a generic class as Class Gen<T> and then in a non-generic class class GenDemo (which includes the main()). Then, you can create Gen references for different data types, such as Gen<Integer> iOB and Gen <String> strOB.
However, in the example given on creating a generic method, the book gives the following code:
//This is a simple generic method
class GenMethDemo
{
//determine if an object is in an array
static<T,V extends T> boolean isIn(T x, V[] y)
{
for (int i=0; i<y.length; i++)
if(x.equals(y[i]))
return true;
else
return false;
}
public static void main(String[] args)
{
//use isIn() on Integers
Integer nums[]={1,2,3,4,5};
if(isIn(2,nums))
System.out.println("2 is in nums");
if(!isIn(7,nums))
System.out.println("2 is in nums");
//use isIn() on Strings
String strs[]={"one", "two", "three", "four", "five"};
if(!(isIn("two", strs))
System.out.println("two is in strs");
}
}
I understand that this program is trying to determine if a given array consists of a specified object. But I can't wrap my head around this line:
static <T,V extends T> boolean isIn(T x, V[] y)
Thinking on the lines of what I've studied so far in generics, I know that there are two arguments of the Boolean function isIn(). But what are the type-paramaters <T, V extends T> doing before the return type of the function isIn()?
I understand the use of the keyword extends here; it acts as a bound for the type-parameter V, ie: V must be of the same type as T or a subclass of T, but I can't get further.
There is a similar use of type-parameters under the topic: Creating Generic Constructors, as:
class GenCons
{
private double val;
<T extends Number> GenCons(T arg)
{
val=arg.doubleValue();
}
void showVal()
{
System.out.println("Val: "+ val);
}
}
As before, I'm stumped by the line: <T extends Number> GenCons(T arg). Why is <T extends Number> used before the constructor is declared? It could also have been written like: GenCons(<T extends Number> arg)?
Any help would be highly appreciated.
Notice how in your GenMethDemo and GenCons classes, the class itself doesn't have a generic type. It's not class GenMethDemo<T, V extends T> -- instead, it's just class GenMethDemo.
So if GenMethDemo and GenCons aren't generic classes, how are you able to use generics? It seems to be a contradiction.
Well, Java also lets you define generic methods. If I do static<T,V extends T> boolean isIn(T x, V[] y), it's as if I had actually done class GenMethDemo<T, V extends T> except that the type variables T and V are scoped only to that particular method. This can be useful when you don't necessarily want the entire class to use generics; only a method or two where they're really needed.
I'm new to Java, and even newer to generics in Java. I have looked for similiar questions, but have found no straightforward answer for my particular problem.
I'm developing a project to manage the patients, doctors, consultations, medical events and all the stuff that is associated with a medical clinic.
What I am trying to do right now is to create a list of medical events associated to each patient. To this list of medical events, for now, it is only supposed to be allowed to add exams and prescriptions, but it's supposed to be extendable: I want to be able to add other types of medical events in the future if I need to, such as information about surgeries.
So, I started by creating an ArrayList of generic ArrayLists in the class Patient, with its type bounded to be extended by the class MedicalEvent (so, for now, it's an ArrayList of ArrayLists of type either Prescription or Exam). I also created an ArrayList of type Prescription and another of the type Exam.
List<ArrayList<? extends MedicalEvent>> medicalevents;
private ArrayList<Prescription> prescriptions;
private ArrayList<Exam> exams;
Then, in the constructor, I added the ArrayLists prescriptions and exams to the ArrayList medicalevents.
medicalevents.add(prescriptions);
medicalevents.add(exams);
To add medical events of one of the two allowed types, I defined the following method:
public void addMedicalEvent(E element){
if(element instanceof Prescription){
medicalevents.get(0).add((Prescription)element);
}
if(element instanceof Exam){
medicalevents.get(1).add((Exam)element);
}
}
The problem is, I get the error "The method add(capture#1-of ? extends MedicalEvent) in the type ArrayList is not applicable for the arguments (Prescription)" and I don't know what it means. Can anyone tell me what I am doing wrong, or suggest a better way to tackle this problem?
Thanks!
Given following declarations
class A {}
class B extends A {}
class C extends A {}
public class SOSample {
public static void main(String[] args) {
List<List<? extends A>> list = new ArrayList<List<? extends A>>();
final List<? extends A> as = list.get(0);
as.add(new B()); // error here
}
}
you can't add B to as, because it will cause problem later on, when you'll try to read from the list:
A a = list.get(0).get(0); // is a B or C?
To better understand this problem there's funny example:
class Vehicle { void launch(); }
class Car extends Vehicle {}
class NuclearMissile extends Vehicle {}
...
// this is prohibited because of below code
List<? extends Vehicle> cars = new ...
// imagine this is possible...
cars.add(new NuclearMissile());
// now this is possible
cars.get(0).launch();
Generally, collections with bounded wildcards like List<? extends Something> are useful for code which won't modify collection, but just iterate over it doing something with elements.
Regarding your original problem -- you can change code so there are two distinct lists, one for Prescription, another for Exam. You still can have just one method which will iterate over these two lists doing something useful (like printing theirs contents):
void doSomethingWithEvents(List<? extends Event> events) {
}
Would something like this work better?
class Medical {
List<EventList<?>> eventLists = new ArrayList<EventList<?>>();
Medical() {
eventLists.add(new EventList<Perscription>(Perscription.class));
eventLists.add(new EventList<Exam>(Exam.class));
}
boolean add(Object item) {
for(EventList<?> list : eventLists) {
if(list.tryToAdd(item)) {
return true;
}
}
return false;
}
}
class EventList<T> {
Class<T> type;
List<T> list = new ArrayList<T>();
EventList(Class<T> type) {
this.type = type;
}
boolean tryToAdd(Object item) {
if(type.isInstance(item)) {
list.add(type.cast(item));
return true;
} else {
return false;
}
}
}
I'm making a set of custom swing components that implement various properties like a required flag or tabIndex. I'm having problems when trying to populate a List of the various custom components, and then sorting the list based on the tabIndex of each component.
How I'm trying to do this is by having my components implement an interface called Indexed which implements a single method getIndex(). Then sorting using my IndexedComparator.
My classes:
Indexed:
public interface Indexed {
public int getIndex();
}
IndexedComparator:
public class IndexedComparator implements Comparator<Indexed> {
#Override
public int compare(Indexed o1, Indexed o2) {
return o1.getIndex() - o2.getIndex();
}
}
WWTextField:
public class WWTextField extends JTextField implements Indexed, FocusListener {
private boolean required;
private int tabIndex;
...
#Override
public int getIndex() {
return tabIndex;
}
}
NewJFrame:
public class NewJFrame extends JFrame {
List<? extends Component & Indexed> list = new ArrayList<>();
IndexedFocusTraversalPolicy policy = new IndexedFocusTraversalPolicy();
public NewJFrame() {
initComponents();
list.add(wWTextField1);
list.add(wWTextField2);
list.add(wWTextField3);
list.add(wWTextField4);
list.add(wWTextField5);
list.add(wWFormatedTextField1);
list.add(wWFormatedTextField2);
Collections.sort(list);
policy.populateComponents(list);
this.setFocusTraversalPolicy(policy);
}
}
Edit: I forgot to post an actual question. Why doesn't my implementation of
List<? extends Component & Indexed> list = new ArrayList<>();
Work? When I try to compile I get these errors:
NewJFrame.java:22: error: > expected
NewJFrame.java:22: error: ';' expected
NewJFrame.java:22: error: illegal start of type
Comparator instances are not "automatic", you have to specify them. I think you mean:
Collections.sort(list, new IndexedComparator());
Constraints are only allowed where type parameters are declared (i.e., class C< T extends I & J > {}, interface E< T extends I & J > {}, < T extends I & J > void f() {}).
You are thus reduced to giving intersection types names. This non-feature is one of the many things that bug me about Java.
I suppose NewJFrame could be generic in T extends Component & Indexed, and list could have type List< T >. In some cases type inference could let client code avoid specifying the specific type.
I've been looking around to see if I find something to help me with my problem, but no luck until now. I've got the following classese:
public interface ISort<T> {
public List<T> sort(List<T> initialList);
}
public abstract class Sort<T> implements ISort<T> {
private Comparator<? super T> comparator;
public Sort(Comparator<? super T> comparator) {
this.comparator = comparator;
}
#Override
public List<T> sort(List<T> initialList) {
ArrayList<T> list = new ArrayList<T>(initialList);
Collections.sort(list, comparator);
return list;
}
}
public abstract class InternalTreeItem<T> {
public abstract String getValue();
}
public class D extends InternalTreeItem<Integer> {
private Integer i;
public D(Integer i) {
this.i = i;
}
#Override
public String getValue() {
return i.toString();
}
public Integer getInteger() {
return i;
}
}
public class DComparator implements Comparator<D> {
#Override
public int compare(D o1, D o2) {
return o1.getInteger() - o2.getInteger();
}
}
public class DSort extends Sort<D> {
public DSort(Comparator<D> comparator) {
super(comparator);
}
public DSort() {
super(new DComparator());
}
}
And the test class:
public class TestClass {
#Test
public void test1() {
List<InternalTreeItem<?>> list= new ArrayList<InternalTreeItem<?>>();
list.add(new D(1));
list.add(new D(10));
list.add(new D(5));
ISort<?> sorter = new DSort();
sorter.sort(list);
}
}
The compiler gives an error at the line
sorter.sort(list);
and states
The method sort(List<capture#2-of ?>)
in the type ISort<capture#2-of ?>
is not applicable for the arguments
(List<InternalTreeItem<?>>)
Ok, after a couple of hours and help from a friend, we realized the problem lies with Collections#sort(List<T> list, Comparator<? super T> c) in the abstract class Sort, as I use a Comparator<? extends T>.
I use generics, as I have 2 models, one model's super class is a generic abstract subclassed by 35 classes, and the second model actually has 2 different super classes, which combined, are subclassed by again 35 classes. These hierarchies are given, there's nothing I can do to modify them.
The model here is very simple, but you get the point. Also, there's a factory, that depending on the type of T, returns one sorter, or another.
Can any one please help and provide a solution for my issue (that is to sort a generic list; the parameter type can be a generic superclass or one of it's subclasses).
Thanks and best regards,
Domi
One way to approach this is to use a wrapper class for the classes that you cannot change.
So in your example you want to order a list of object D, based on an Integer value. By putting your objects in a wrapper and then adding this to the list, you can expose the value you wish to sort the list by.
For example, you could define an interface like:
private interface SortableListItem<T> extends Comparable<SortableListItem<T>> {
public T getValue();
}
Then, create a wrapper class for D:
public class DWrapper implements SortableListItem<Integer> {
private D item;
public DWrapper(D item) {
this.item = item;
}
public Integer getValue() {
return item.getInteger();
}
public int compareTo(SortableListItem<Integer> o) {
return getValue().compareTo(o.getValue());
}
}
From here it is pretty simple to create and sort your list:
D item1= new D(1);
D item2= new D(10);
D item3= new D(5);
DWrapper wrapper1 = new DWrapper(item1);
DWrapper wrapper2= new DWrapper(item2);
DWrapper wrapper3= new DWrapper(item3);
List<SortableListItem<Integer>> sortableList = new ArrayList<SortableListItem<Integer>>();
sortableList.add(wrapper1 );
sortableList.add(wrapper2);
sortableList.add(wrapper3);
Collections.sort(sortableList);
You can of course make the wrapper class accept a more generic object - the key is that each object returns a value (in this case an Integer) that the List can be sorted by.
The variable sorter is of type ISort<?>. It could have, say, an ISort<String> assigned to it. The sort method takes an argument of List<T> where T could be String. Clearly you cannot use List<InternalTreeItem<?>> for List<String>, so fortunately the compiler points out the error.
(Note: It's generally a good idea to keep to coding conventions. No I Hungarian prefixes, or single letter class names.)
Running your code what I can deduce is that you get a compile error since it is not possible to capture the wildcard that you specify in below line of class TestClass:
ISort<?> sorter = new DSort();
As I understand an occurrence of wild card is taken to stand for some unknown type and from your code it is not possible to infer the type (for the compiler).
But looking at the code, the class DSort is not written in a way to take type parameters
and any attempt to pass type parameters during creation of instance of DSort gave the error:
The type DSort is not generic; it cannot be parameterized with arguments
But you mention that you cannot alter the code of the modules (i.e I presume of classes DSort etc).
So one way to fix the error would be to not use generics during creation of instance of ISort.
The below code works and the prints the the sorted output (1,5,10)
List<InternalTreeItem<?>> list= new ArrayList<InternalTreeItem<?>>();
list.add(new D(1));
list.add(new D(10));
list.add(new D(5));
// no generic arguments
ISort sorter = new DSort();
List<InternalTreeItem<?>> sortedList = sorter.sort(list);
for(InternalTreeItem i:sortedList) {
System.out.println(i.getValue());
}
but results in a warning of the form ISort is a raw type. References to generic type ISort should be parameterized. But having code that uses generic and having warning of this form is not a good practice . This warning implies that the compiler cannot give cast-iron guarantee about the implicit casts it does to use generics.
If feasible, I think the better solution would be to see how the modules class can re-designed.