Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8244074

Using newInstance constructor breaks type safety

XMLWordPrintable

      A DESCRIPTION OF THE PROBLEM :
      Using the newInstance constructor it is possible to construct a map with keys that do not extend the declared key type.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Full source code provided.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      newInstance should have thrown a runtime exception since the input types do not match the declared types. With the given source code ideally "Exception was caught" should be printed.
      ACTUAL -
      newInstance creates a map with keys whose type does not extend the declared key type of the map. With the given source code we see a map of <Dog, String> contain a key of type Cat.

      ---------- BEGIN SOURCE ----------
      import java.util.HashMap;
      import java.util.Map;

      public class Main {

          public static void main(String[] args) {
              final Map<Animal, String> animalToName = new HashMap<>();
              final Cat cat = new Cat( 1, "fish" );
              final Dog dog = new Dog( 2, 10 );
              animalToName.put( cat, "catName" );
              animalToName.put( dog, "dogName" );

              try {
                  final DogNames dogNames = (DogNames) DogNames.class.getConstructors()[0].newInstance( animalToName );
                  System.out.println( "No Exception caught" );
                  System.out.println( dogNames.getDogToName() );
              } catch( final Exception e ) {
                  //Ideally an exception would be thrown here and this statement would be printed, however that is not the case.
                  System.out.println( "Exception was caught" );
              }
          }

          private static class DogNames {

              private final Map<Dog, String> dogToName;

              public DogNames( final Map<Dog, String> dogToName ) {
                  this.dogToName = dogToName;
              }

              public Map<Dog, String> getDogToName() {
                  return this.dogToName;
              }

          }

          private static class Dog extends Animal {

              private final int woofLoudness;

              public Dog( final int weight, final int woofLoudness ) {
                  super( weight );
                  this.woofLoudness = woofLoudness;
              }

              @Override
              public String toString() {
                  return String.format( "Dog with weight: %d, woof loudness: %d", this.weight, this.woofLoudness );
              }

          }

          private static class Cat extends Animal {

              private final String favoriteFood;

              public Cat( final int weight, final String favoriteFood ) {
                  super( weight );
                  this.favoriteFood = favoriteFood;
              }

              @Override
              public String toString() {
                  return String.format( "Cat with weight: %d, favorite food: %s", this.weight, this.favoriteFood );
              }

          }

          private static class Animal {

              protected final int weight;

              public Animal( final int weight ) {
                  this.weight = weight;
              }

          }

      }

      ---------- END SOURCE ----------

      FREQUENCY : always


            darcy Joe Darcy
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: