-1

Before generics

List list = new ArrayList<>();
list.add(new Manager(2));

for(Object obj : list){
   System.out.println(((Clerk)obj).display1());
}

// will lead to class cast exception.

Generics was derived for avoiding casting. I have written some code which gives a ClassCastException even after using generics.

class Employee{
    int age;
    Employee(int age){
        this.age = age;
    }

    public void display(){
        System.out.println("age:"+age);
    }
}

class Manager extends Employee{
    int age;
    Manager(final int age) {
        super(55);
        this.age = age;
    }

    public void display(){
        System.out.println("age:"+age);
    }
}

class Clerk extends Employee{
    int age;
    Clerk(final int age) {
        super(55);
        this.age = age;
    }

    public void display1(){
        System.out.println("age:"+age);
    }
}
public static void main(String[] args) {

    List<Manager> list1 = new ArrayList<>();
    list1.add(new Manager(22));

    test(list1);

}

private static void test(List<? extends Employee> list){
    for(Employee employee1 : list){
        ((Clerk)employee1).display1();
    }
}

In the method testI have used lower bounded generic parameter. I have passed List of manager which is subclass of employee, but in test I have provided implementation for another subclass of employee, class clerk which causes class cast exception.

Exception in thread "main" java.lang.ClassCastException: class generics.Manager cannot be cast to class generics.Clerk (generics.Manager and generics.Clerk are in unnamed module of loader 'app')
    at generics.GenericsDemo.test(GenericsDemo.java:56)
    at generics.GenericsDemo.main(GenericsDemo.java:50)

I am able to compile and execute code means Java supports this feature and i can use it in production. If I have no access to any caller method, any class nothing and I need to provide implementation of test method then how should I handle this exception?

Should I use instanceof before typecasting? I want to understand technically how to handle and not fundamentally it is correct or incorrect, just how to handle exception.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
  • 2
    *"Generics was derived for avoiding class cast exception"* -- where did you see this? Reference? I always thought that generics were used to help avoid *casting* itself, and to shift certain errors from run-time into compile-time. – Hovercraft Full Of Eels May 30 '21 at 10:54
  • 3
    You try to cast a `Manager` into a `Clerk`. This cannot work since `Manager` is not a sub-class of `Clerk`. Hence the `ClassCastException`. – Turing85 May 30 '21 at 10:56
  • If I am implementing a method and will take parameter List extends Employee> and I have no access to source code of caller method nor class class structure then to avoid this exception I need to do manual check if(employee instanceOf Clerk) before using ((Clerk)employee).display1(); But my understanding till now is to avoid instanceOf check we go for generics. Is my understanding incorrect? –  May 30 '21 at 11:03
  • My above scenario is valid and so do I need to manually do instanceOf check for avoiding this kind of exception or java generics provide some api for it? –  May 30 '21 at 11:04
  • I'm not sure your ultimate goal with this code, but if you're looking to do something like "double-dispatch" then consider use of the "Visitor Design Pattern" – Hovercraft Full Of Eels May 30 '21 at 11:09
  • 2
    You got it backwards: if you need to use `instanceof`, your code probably shouldn't be generic. – Sweeper May 30 '21 at 11:09
  • @HovercraftFullOfEels my only goal is to understand if above like scenario occurs in production application then how to avoid class cast exception? Do a manual check? or anything specific provided by java to handle this? –  May 30 '21 at 11:24
  • 2
    @DeepakAgrawal `list1.stream().filter(Clerk.class::instanceOf).map(Clerk.class::cast).forEach(Clerk::display1);` – Turing85 May 30 '21 at 11:28
  • @Turing85 means I need to do a manual check in such scenario because there can be a possibility of exception. Thanks a lot everyone for helping me in this. –  May 30 '21 at 11:29
  • 1
    @DeepakAgrawal To avoid: Don't cast anything (including all the new pattern matching stuff), take heed of warnings and turn all the lints on (`-Xlint:all`). – Tom Hawtin - tackline May 30 '21 at 11:36
  • 1
    @HovercraftFullOfEels Generics in Java were originally sold as getting rid of `ClassCastException`s. I pointed out at the time that there were seven times as many hits in the Bug Parade for `NullPointerException` which isn't covered. – Tom Hawtin - tackline May 30 '21 at 11:41
  • @TomHawtin-tackline can you please elaborate more over your comment you tagged me in. If I should not cast anything then can you please show with an example how to to avoid class cast exception in this case. –  May 30 '21 at 11:48
  • @DeepakAgrawal Don't loose the type information you need. If you need `obj` to be a `Clerk` type it as such, and `list` as `List` (or `List extends Clerk>`). – Tom Hawtin - tackline May 30 '21 at 12:23
  • @TomHawtin-tackline List extends Employee> can be any child class of Employee. I dont know at runtime what child class will be present in list. So then if i want to implement the method which is not present in parent class in this case Employee then my understanding is to first check whether the object on which i am calling this method is actually in list. this i can do with .getClass() method i guess. This is what my knowledge is limited upto. Please correct me with alternative way if any –  May 30 '21 at 12:30
  • 1
    You tell Java to expect any `Employee`, then you cast without checking to `Clerk`, if you explicitly cast, then Java will believe that you know what you're doing. If you're not sure if it is a `Clerk`, then you should first use `instanceof` to check before handling something as a `Clerk`. – Mark Rotteveel May 30 '21 at 12:34
  • Thanks @MarkRotteveel. "if you explicitly cast, then Java will believe that you know what you're doing." this was the bridge that I was missing when learning the concept. Got your point. –  May 30 '21 at 12:48
  • One issue that hasn't been mentioned is that you're using a raw type `List`: that's a [very bad idea](https://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it) and is usually done by accident. Avoid it at all cost. – Joachim Sauer May 30 '21 at 12:51

1 Answers1

1

Your code is fundamentally wrong.

You create a Manager and then later expect to treat it as a Clerk.

A Manager is not a Clerk, and that's the end of it.

Even if the List were a List of Employees, once you create a Manager, it's a Manager, and it's still a Manager when it's in the List of Employees. You can't just decide you want a Manager to be a Clerk.

Possible casts in your class hierarchy:

  1. Manager to Employee (usually implicit)

  2. Clerk to Employee (usually implicit)

  3. Employee to Manager (if and only if the object is actually a Manager)

  4. Employee to Clerk (if and only if the object is actually a Clerk)

To be more specific about where the wrongness is, it is this method:

private static void test(List<? extends Employee> list){
    for(Employee employee1 : list){
        ((Clerk)employee1).display1();
    }
}

The method declaration says that it can handle a list of Employees or objects sublassed from Employee.

The method is implemented so that it will throw an exception if the list contains any Employee that is not actually a Clerk.

Assuming that throwing such an exception is not actually the intent of the 'test' method, then the implementation and declaration need to be compatible. Why is the method not defined as

private static void test(List<Clerk> list)

since that is all it can handle, and defining it thus allows compile-time validation of type.

Alternatively, just treat them as the Employees they are. Any Employee can be displayed.

private static void test(List<? extends Employee> list){
    for (Employee employee : list) {
        employee.display();
    }
}
iggy
  • 1,328
  • 4
  • 3
  • Fundamentally it is incorrect. Purpose of me asking question is to understand the technicals. Technically the code compiles and gives output means java supports this kind of code, means i can use it in production. So keeping aside what is fundamentally or conceptually correct or incorrect my only thing is how to handle exception in such case –  May 30 '21 at 12:22
  • 1
    "How to handle it" depends on what you expect to do. If you expect to give up if the list contains a single non-Clerk, then try/catch solves it. But you'll possibly -- for a mixed list of Managers and Clerks -- have displayed some arbitrary number of Clerks before that point, so it's not clean. I'm at a loss to understand the rationale behind having a List and feeding it to something that cannot handle Managers. How did the design get so messed up? – iggy May 30 '21 at 12:29
  • I am just trying to learn and experiment with concept. Generics is my week point so trying to experiment the features and get advice from experts on stack overflow. –  May 30 '21 at 12:33
  • 1
    Fair enough; I was perhaps reacting to the words 'use it in production'. To distil the lesson down: a generic implementation should only use aspects that are in common to the generic type (not sure if my terminology is accurate here), If you have a list of Employees, or of anything derived from Employees, then you should treat them as Employees, as objects of some superclass (here just Object), but not as objects of some subclass (Clerk/Manager), because they may not be of that subclass. – iggy May 30 '21 at 12:43