-4

I can't call method serve() below.

public class GenericService {

   public static class Service<T> {

      public void serve(T t) {
         System.out.println(t.toString());
      }

   }

   public static Service<?> service = new Service<String>();

   public static void main(String[] args) {

      service.serve("Hello World!"); // 'serve(capture<?>)' cannot be applied to '(java.lang.String)'

   }
}

How to call this method by force?

Why does Java dislike such calls?

UPDATE

The problem is not ClassCastException as was proposed, because in that case I would be able to write

      try {
         service.serve("Hello World!"); // 'serve(capture<?>)' cannot be applied to '(java.lang.String)'
      }
      catch (ClassCastException e) {
         System.err.println("You see!? This is why I was disliking your code!");
      }

but I can't.

Why?

UPDATE 2

Now, when everybody said out, a new version:

   public static Service<? extends String> service = new Service<String>();

   public static void main(String[] args) {
      service.serve("Hello World!"); // 'serve(capture<?>)' cannot be applied to '(java.lang.String)'

      ((Service<String>)service).serve("Hello World!");  // Unchecked cast: 'GenericService.Service<capture<? extends String>>' to 'GenericService.Service<String>'
   }

what problem is here (don't regard that String is final)?

Dims
  • 47,675
  • 117
  • 331
  • 600
  • 2
    You cannot, that's the point. What if it were a `Service`? You have said wanted a `Service` of type "I don't care", which means that it could be anything. The fact that you assigned `Service` is neither here nor there... – Boris the Spider Aug 16 '16 at 14:06
  • Why? `service` variable references an object, which CAN process `String`. Why can't I TELL it to do this? – Dims Aug 16 '16 at 14:07
  • What's the definition of the service class? It looks like serve takes a `capture`, not a string. – puhlen Aug 16 '16 at 14:07
  • Why not make `service` a reference type `Service`? – Zircon Aug 16 '16 at 14:07
  • @puhlen defenitions are all there; an example is self containing – Dims Aug 16 '16 at 14:08
  • @Zircon why should I? By what reason? – Dims Aug 16 '16 at 14:08
  • Maybe the example is over-simplifying your scenario, but if you know `service` poiints to a `Service`, there's no reason to make the reference type anything different. – Zircon Aug 16 '16 at 14:09
  • @Zircon I am asking from service usage side, not from service providing – Dims Aug 16 '16 at 14:11
  • Lets approach this a different way; why do you thing that you should be able to call `serve` with a `String` on a `Service>`? – Boris the Spider Aug 16 '16 at 14:12
  • @BoristheSpider this is my question: what if there were `Service`? – Dims Aug 16 '16 at 14:13
  • @BoristheSpider because of the first law of robotics: machine must obey direct orders of a human – Dims Aug 16 '16 at 14:14
  • 1
    @Dims [2nd law](https://en.wikipedia.org/wiki/Three_Laws_of_Robotics). Come on! And in this case, the 1st law applies - doing what you ask would mean that you come to harm; via a `ClassCastException`. – Boris the Spider Aug 16 '16 at 14:17
  • @BoristheSpider no, this is NOT the case; otherwise hint me how to cause `ClassCastException` here. – Dims Aug 16 '16 at 14:19
  • 1
    Because you could have declared it as a `Service`, then `service.serve("Hello World!")` would cause a `ClassCastException`; agreed? So what makes your assignment of `Service` any different? The compile time type of your `Service` is still the same. – Boris the Spider Aug 16 '16 at 14:21
  • @BoristheSpider no, I disagree. If I pass string to `Service` it will also print some result, because all classes have `toString()` method. So, no any place to raise `ClassCastException`. – Dims Aug 16 '16 at 14:24
  • Incidentally, with your laws you have created a [robot hellscape](https://xkcd.com/1613/). Maybe don't program robots; please. – Boris the Spider Aug 16 '16 at 14:24
  • But you have said that your `Service` method takes some type `T`, which is a generic type. You have then said that the `T` is a `Foo`. Then you pass in a `String`. Bang. – Boris the Spider Aug 16 '16 at 14:25
  • @BoristheSpider I will, don't even ask :D – Dims Aug 16 '16 at 14:29
  • Your next problem is [PECS](https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super). Again, the wildcard does not mean "any" type it means "some, unknown" type - it could be a `Service` where `Bar extends String`. The fact that `String` is `final` is not taken into account. – Boris the Spider Aug 16 '16 at 14:35
  • @BoristheSpider absolutely – Dims Aug 16 '16 at 14:37

2 Answers2

8

You misunderstand what the wildcard means (this is actually a common misunderstanding about generics wildcards in Java).

Service<?> does not mean: a Service that can accept any type.

It does mean: a Service of a specific, but unknown type.

You cannot call serve, passing it a String, because the type that the ? stands for is unknown - the compiler cannot check, just by looking at the type of the variable service, if the actual service that it refers to is a Service<String>, a Service<Integer> or a Service<Whatever>, so it can't know if it should be allowed to pass a String to the serve method.

To keep it type-safe, the compiler has no other option than to not allow you to call the method.

How to call this method by force?

You can force it by casting:

((Service<String>) service).serve("Hello World!");

(But keep in mind that casting means you are giving up on type-safety, in general you should avoid casting as much as possible).

Jesper
  • 202,709
  • 46
  • 318
  • 350
2

Wildcards in generics has not for aim to allow the usage of any type. It is a way to identify an unknown and invariable type.

In your code, ? is of type String but the compiler can't guess it. You could change it later with a Service<Integer> and have ClassCastException at runtime by forcing String objects in it.

There is no way to cast an object to a captured type (= wildcard).

If you want to accept every object that declare the toString() method then you should use Service<Object> instead of Service<?>:

public static Service<Object> service = new Service<Object>();
Raphaël
  • 3,646
  • 27
  • 28