-1

So, I have 2 classes: Cat and Kitten extends Cat. They have fields (int age, String name) and a constructor. I am trying to create my Functional interface that takes Kitten as input parameter and returns me a Cat (and makes it 2 years elder).

@FunctionalInterface
public interface CatToKittenFunction <Kitten, Cat>{
Cat grow(Kitten k);
}

And Main class

public static void main(String[] args) {
    Cat smallCat = new Kitten(1, "Push");
    Cat oldCat = grow(s -> s.getClass(), (Kitten)smallCat);
    System.out.println("oldCat.getClass() = " + oldCat.getClass());
}

private static Cat grow(CatToKittenFunction function, Kitten kitten){
    return (Cat) function.grow(kitten);
}

I don't have an idea where I can change its age and I have ClassCastException as well. What am I do wrong?

Tom
  • 73
  • 1
  • 8
  • 2
    Why does `CatToKittenFunction` have type variables `Kitten` and `Cat`? Surely it's then just a `SomethingToSomethingElseFunction`. – Andy Turner Sep 09 '21 at 13:53
  • 2
    What is the relationship between the types `Cat` and `Kitten`? And shouldn't your interface be called `KittenToCatFunction` ? – khelwood Sep 09 '21 at 13:54
  • If `Kitten extends Cat` (which it would need to do for `Cat smallCat = new Kitten(1, "Push");` to work) you could just return a `Cat`. But if you want to return something like an actual `Cat` instance then use a lambda like `kitten -> new Cat(kitten.getAge() + x, kitten.getName())` (where `x` is some amount of years you want the cat to grow by). – Thomas Sep 09 '21 at 13:54
  • BTW, `CatToKittenFunction function` is a raw type. You should pay attention to compiler warnings, this is why you get a `ClassCastException` rather than it failing to compile. – Andy Turner Sep 09 '21 at 13:54
  • 1
    Note that the type variables `` make this a very confusing piece of code, since you apparently also have classes named `Kitten` and `Cat`. Note that `Kitten` and `Cat` in the interface do **not** refer to those classes, but just to the type variables which confusingly have the same names as the classes. You probably do not need those type variables at all. – Jesper Sep 09 '21 at 14:00
  • Completely unrelated to the question - but shouldn't it be *KittenToCat*? You are growing the kitten, not sending the cat through a time machine. – Chris Parker Sep 09 '21 at 14:46

3 Answers3

2

What am I do wrong?

There are a few things wrong, but the compiler isn't stopping you doing them because you're using a raw type (see: What is a raw type and why shouldn't we use it?):

CatToKittenFunction function

CatToKittenFunction is an interface with two type variables. You either have to specify two type parameters (correct usage); or no type parameters (incorrect usage, this makes it a raw type).

Raw types disable type checking of things using them. So, rather than this being, say, a function which turns a Kitten into a Cat, it's a function which turns an Object into an Object. This is why you have to explicitly cast to Cat with (Cat).

Because it returns an Object, the compiler doesn't know whether casting it to Cat will fail. It could succeed (all Cats are Objects), but it could also fail (not all Objects are Cats). Fortunately, the cast to Cat is a checked cast, so it will immediately detect if it's not a Cat, and throw a ClassCastException.

A ClassCastException is bad, insofar as it's a runtime failure; but it's at least good insofar as it is thrown very close to where the problem occurs. The failures that tend to result from raw types, heap pollution, can occur far from and long after the problem was actually introduced.

The CatToKittenFunction you're passing in is not a CatToKittenFunction<Kitten, Cat>, it's a CatToKittenFunction<Object, ? extends Class<?>> - you're taking in some object, returning its class. A Class isn't a Cat, so this cast is going to fail with a ClassCastException.

If you expect your function to take in a Kitten, and return a Cat, specify that in the parameter type:

CatToKittenFunction<Kitten, Cat> function

or, drop the type variables from the interface:

public interface CatToKittenFunction {
  Cat grow(Kitten k);
}

so this always converts a Kitten to a Cat.

With either approach, now your lambda s -> s.getClass() simply won't compile.

You just need to work out how to create your Cat from your Kitten. For example:

s -> new Cat(s.age() + 1, s.name())
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
1
  • You can try implementing this by using your functional interface :

  • Below Lambda expression will take Kitten as input and make it's age 2 year elder

     CatToKittenFunction<Kitten, Cat> fn = (Kitten k) ->{
          return new Cat(k.age+2,k.name);
      };
    
  • Call the grow function by passing kitten object and it will returns the Cat incremented age with 2 year elder.

      Cat oldCat = fn.grow(smallCat);
      System.out.println("oldCat.age: " + oldCat.age);
      System.out.println("smallCat.age: " + smallCat.age);
    
0

Solved it this way:

    Kitten smallCat = new Kitten(1, "Push");

    KittenToCatFunction function = x -> new Cat(x.getAge() + 2, x.getName());
    Cat oldCat = function.grow(smallCat);

    System.out.println("oldCat.getClass() = " + oldCat.getClass()); //.Cat
    System.out.println("oldCat.getAge() = " + oldCat.getAge()); //3
    System.out.println("oldCat.getName() = " + oldCat.getName()); //Push
Tom
  • 73
  • 1
  • 8