-1

I reading a book about OOP patterns with Java examples.. Class code example from book:

public class Board {

    private int width, height;
    private List tiles;

    ...

    private void initialize() {
        tiles = new ArrayList(width);
        for (int i=0; i<width; i++) {
            tiles.add(i, new ArrayList(height));
            for (int j=0; j<height; j++) {
                ((ArrayList)tiles.get(i)).add(j, new Tile());
            }
        }
    }

    public Tile getTile(int x, int y) {
        return (Tile)((ArrayList)tiles.get(x-1)).get(y-1);
    }

}

Question:

Why in both described methods used Explicit type conversion? In first case to ArrayList in line ((ArrayList)tiles.get(i)).add(j, new Tile());, in second to Tile in line return (Tile)((ArrayList)tiles.get(x-1)).get(y-1);. I mean, this is script author opinion or necessary in Java case?

Community
  • 1
  • 1
slava
  • 791
  • 1
  • 11
  • 26
  • 3
    This is necessary because you're dealing with raw types. it's not necessary to cast if you leverage generics. – Ousmane D. Dec 13 '18 at 19:14
  • This is why generics were added in 2004 to solve this sort of problem. – Peter Lawrey Dec 13 '18 at 19:17
  • The best way to tell if it's needed is to try this out, without the casts, and see whether it works. Incidentally, I think you're reading a very old book. Java has changed a fair bit since this example made sense. – Dawood ibn Kareem Dec 13 '18 at 19:20
  • @DawoodibnKareem ibn Kareem Code is not completed. Just parts as examples. I can't check it. – slava Dec 13 '18 at 19:22
  • @Aomine Can you please explain for Java newbie. I need to learn patterns, but I must read this book on programming language which i not so familiar. – slava Dec 13 '18 at 19:25
  • @mature there's more information regarding raw types and generics in my answer. I cannot talk about them in detail because it's a big topic but i can answer your questions and refer you to good documentation. – Ousmane D. Dec 13 '18 at 19:26
  • 1
    If you're looking to learn design patterns, I recommend _Head First Design-Patterns_ from O'Reilly. – Don Hosek Dec 13 '18 at 19:28
  • @DonHosek, Yep, this will next. Now this example from O'Reilly Media - Head First Object-Oriented Analysis and Design – slava Dec 13 '18 at 19:30

5 Answers5

3

This is older Java code, before the introduction of generics, so the List interface returns Object rather than the desired type. It looks like, in modern parlance, they want to have something along the lines of

private List<List<Tile>> tiles;

and then initialize (assuming I made no typos) could look like:

private void initialize() {
    tiles = new ArrayList<>(width);
    for (int i=0; i<width; i++) {
        tiles.add(i, new ArrayList<>(height));
        for (int j=0; j<height; j++) {
            tiles.get(i)).add(j, new Tile();
        }
    }
}

(I believe the Array in the original code is a typo for ArrayList)

and

public Tile getTile(int x, int y) {
    return tiles.get(x-1).get(y-1);
}
Don Hosek
  • 981
  • 6
  • 23
1

Seems as if you're reading an ancient book.

Why in both described methods used Explicit type conversion?

This is necessary because you're dealing with raw types

I mean, this is script author opinion or necessary in Java case?

it's not necessary to cast if you leverage generics.

Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
1

There are two ways to declare an ArrayList.

For the sake of making a simple example, I have made the following dummy class

public class Tile {
    int x = 123;
}

The first older way as you described in your question may be written as follows

List tiles = new ArrayList();
tiles.add(new Tile());

Tile tile = ((Tile)tiles.get(0));
System.out.println(tile.x); // prints 123

The same thing can be accomplished with by using Generics.

List<Tile> tiles = new ArrayList<>();
tiles.add(new Tile());

Tile tile = tiles.get(0);
System.out.println(tile.x); // prints 123

With generics, there is no need to use casting and is the preferred way of using a list.

J.A.P
  • 715
  • 6
  • 13
  • Confused! In first your example (without Generics) you add `tiles.add(new Tile());` certain specific type to `tiles`!! Why is necessary to convert it here `Tile tile = ((Tile)tiles.get(0));` when you get it back?! – slava Dec 13 '18 at 19:36
  • 1
    @mature That is a good question. It is because a List (without using the generic tag <>) Takes an Object. And Object is the parent/super class to every other class. So when it is stored in the list it is treated as an Object rather than a Tile. This is why you need to cast it back to Tile when you retrieve it. When using List the type is enforced when storing and treated as a Tile all the way. – J.A.P Dec 13 '18 at 19:44
1

I tried testing your code. I'm not sure that the code you've specified is correct entirely.

I think

tiles = new Array(width);

should have been

tiles = new ArrayList(width);

Further

 ((ArrayList)tiles.get(i)).add(j, new Tile());

Here tiles.get(i) is typecasted to ArrayList because tiles is a List with each element holding its own ArrayList. (It is a 2D dynamic array creation code )

Currently tiles.get(i) is returning an Object type. It has to be typecasted to ArrayList to use the method "add" Similarly below, object is typecasted to ArrayList to use the method "get"

shellym
  • 546
  • 1
  • 5
  • 11
0

Java is strongly typed language. Most\all type errors must be obtained at compile time. Before Generics you can't control what types stored in List. In this case (Raw types) postponed type checking go to runtime. Then is always better to convert first any raw type to concrete type to avoid runtime errors.

But in this example, we won't even be able to compile the code, because an example shows how unreliable Raw types can be, and the compiler understands this at compile time. I mean this string: ((ArrayList)tiles.get(i)).add(j, new Tile()); - is a simple case that speaks for itself. If we remove (ArrayList) and get (tiles.get(i)).add(j, new Tile()); -- why Java compiler must believe us that in (tiles.get(i)) we have a List or some Object with add() method?! Yes, not must and he doesn't.

To check we can try compile simple similar example with using of Raw types in case List vars:

List listOfLists = new ArrayList();
listOfLists.add(0, new ArrayList());
(listOfLists.get(0)).add("something");  // compile error

But when we say compiller about exact type inside listOfLists.get(0), and then all will be ok:

((ArrayList)listOfLists.get(0)).add("something");  // NO compile error

Generics solve this problem more accurate. They rigidly fix type for List's vars:

List<List> listOfLists = new ArrayList<List>();   // now List only have List's inside
listOfLists.add(0, new ArrayList());
(listOfLists.get(0)).add("something");  // NO compile error

Compiler know listOfLists.get(0) is a List and it have add() method.

slava
  • 791
  • 1
  • 11
  • 26