I am trying to replace the following switch statement with a map.
public ObjGeometry geometry(final Iterable<String> lines) throws IllegalArgumentException {
final List<Vector3f> vertices = Lists.newArrayList();
final List<Vector2f> textures = Lists.newArrayList();
final List<Vector3f> normals = Lists.newArrayList();
final List<Face> faces = Lists.newArrayList();
for (final String line : lines) {
final List<String> lineElements = Arrays.asList(line.split(" "));
final String token = lineElements.get(0);
switch (token) {
case VERTEX:
final Vector3f vertex = createVertex(lineElements);
vertices.add(vertex);
break;
case TEXTURE:
final Vector2f texture = createTexture(lineElements);
textures.add(texture);
break;
case NORMAL:
final Vector3f normal = createNormal(lineElements);
normals.add(normal);
break;
case FACES:
final Face face = createFace(lineElements);
faces.add(face);
break;
}
}
return ObjGeometry.from(vertices, textures, normals, faces);
}
The methods #createVertex,#createTexture,#createNormaland #createFace transforming a string to a special dataType (Vector2f, Vector3f or Face)
What i want to do is... to build a Map Map<TOKEN, Parser>, so I can iterate over that map and call the right parser for a special token (btw. TOKEN is a String like "v", "vt", "vn", or "f").
What i have is:
ExtractVector3f with method #vectorFrom(List<String>) - can transform a List<String> to Vector3f ExtractVector2f with method #vectorFrom(List<String>) - can transform a List<String> to Vector2f ExtractFace with method #faceFrom(List<String>) - can transform a List<String> to Face an interface LineParser
public interface LineParser<T> {
T apply(final List<String> lineElements);
}
a class to parse a vertex
public class ParseVertex extends ExtractVector3f implements LineParser<Vector3f> {
@Override
public Vector3f apply(final List<String> lineElements) {
return vectorFrom(lineElements);
}
}
a class to parse a normal
public class ParseNormal extends ExtractVector3f implements LineParser<Vector3f> {
@Override
public Vector3f apply(final List<String> lineElements) {
return vectorFrom(lineElements);
}
}
a class to parse a texture
public class ParseTexture extends ExtractVector2f implements LineParser<Vector2f> {
@Override
public Vector2f apply(final List<String> lineElements) {
return vectorFrom(lineElements);
}
}
and a class to parse a face
public class ParseFace extends ExtractFaces implements LineParser<Face> {
@Override
public Face apply(final List<String> lineElements) {
return faceFrom(lineElements);
}
}
The next step would be to build the map... something like
Map<String, LineParser> parsers = Maps.newHashMap();
parsers.put("v", new ParseVertex());
parsers.put("vt", new ParseTexture());
parsers.put("vn", new ParseNormal());
parsers.put("f", new ParseFace());
But now i get in trouble. LineParser in this map is a raw type and raw-types should be avoid.
My Question is: How can i build a map, with POJOs in it - which return different data-types on the method which is defined via Interface?
I would not do this, actually. Even if the map is a good idea, done this way you lose the exact type of the objects you build. What are you gonna do with them after that ? How do you know to which collection adding them ? You can't, unless the parsers have access to all collections to pick the right one. This suggests the parsers should know your main class, or methods of this class.
I would just use lambdas :
Consumer<List<String>> handleTexture = args -> textures.add(parseTexture(args))
Consumer<List<String>> handleVertice = args -> vertices.add(parseVertice(args))
...
Map<String, Consumer<List<String>> handlers = new HashMap<>();
handlers.put("vf", handleTexture);
handlers.put("v", handleVertice);
...
for (final String line : lines) {
final List<String> lineElements = Arrays.asList(line.split(" "));
final String token = lineElements.get(0);
handlers.get(token).accept(lineElements.sublist(1, lineElements.size()));)
}
If you want to do it with Java 7 it is syntactically less nice, but the principle is the same. Create a interface like Consumer with a method accept (you can change all the names if you want to make the class more specific to your project, like Parser and parse, just keep the same signature for the method) :
public class Consumer<T> {
public void accept(T t);
}
Then, in your code
Consumer<List<String>> handleTexture = new Consumer<List<String>>() {
@Override
public void accept(List<String> args) {
textures.add(parseTexture(args));
}
}
The rest is just the same !
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With