可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm using RxVertx which is a sort of RxJava along with Java8 and I have a compilation error.
Here is my code:
public rx.Observable<Game> findGame(long templateId, GameModelType game_model, GameStateType state) { return context.findGame(templateId, state) .flatMap(new Func1<RxMessage<byte[]>, rx.Observable<Game>>() { @Override public Observable<Game> call(RxMessage<byte[]> gameRawReply) { Game game = null; switch(game_model) { case SINGLE: { ebs.subscribe(new Action1<RxMessage<byte[]>>() { @Override public void call(RxMessage<byte[]> t1) { if(!singleGame.contains(0) { game = new Game(); // ERROR is at this line singleGames.put(0, game); } else { game = singleGames.get(0); // ERROR is at this line } } }); } } return rx.Observable.from(game); } }); }
The compilation error is: "Local variable game defined in an enclosing scope must be final or effectively final"
I cannot define 'game' as final since I do allocation\set and return it at the end of the function.
How can I make this code compile??
Thanks.
回答1:
I have a Holder
class that I use for situations like this.
/** * Make a final one of these to hold non-final things in. * * @param <T> */ public class Holder<T> { private T held = null; public Holder() { } public Holder(T it) { held = it; } public void hold(T it) { held = it; } public T held() { return held; } public boolean isEmpty() { return held == null; } @Override public String toString() { return String.valueOf(held); } }
You can then do stuff like:
final Holder<Game> theGame = new Holder<>(); ... theGame.hold(myGame); ... { // Access the game through the `final Holder` theGame.held() ....
回答2:
Since you need to not modify the reference of the object you can wrap the Game
in something else.
The quickest (but ugly) fix is to use an array of size 1, then set the content of the array later. This works because the the array is effectively final, what is contained in the array doesn't have to be.
@Override public Observable<Game> call(RxMessage<byte[]> gameRawReply) { Game[] game = new Game[1]; switch(game_model) { case SINGLE: { ebs.subscribe(new Action1<RxMessage<byte[]>>() { @Override public void call(RxMessage<byte[]> t1) { if(!singleGame.contains(0) { game[0] = new Game(); singleGames.put(0, game[0]); } else { game[0] = singleGames.get(0); } } }); } } return rx.Observable.from(game[0]); }
Another similar option is to make a new class that has a Game
field and you then set that field later.
回答3:
Cyclops has Mutable, and LazyImmutable objects for handling this use case. Mutable is fully mutable, and LazyImmutable is set once.
Mutable<Game> game = Mutable.of(null); public void call(RxMessage<byte[]> t1) { if(!singleGame.contains(0) { game.mutate(g -> new Game()); singleGames.put(0, game.get()); } else { game[0] = game.mutate(g->singleGames.get(0)); } }
LazyImmutable can be used to set a value, lazily, once :
LazyImmutable<Game> game = LazyImmutable.def(); public void call(RxMessage<byte[]> t1) { //new Game() is only ever called once Game g = game.computeIfAbsent(()->new Game()); }
回答4:
You cant. At least not directly. U can use a wrapper class however: just define a class "GameContainer" with game as its property and foward a final reference to this container instead.
回答5:
@dkatzel's suggestion is a good one, but there's another option: extract everything about retrieving/creating the Game into a helper method, and then declare final Game game = getOrCreateGame();
. I think that's cleaner than the final array approach, though the final array approach will certainly work.
回答6:
Although the other approaches look acceptable, I'd like to mention that you can't be sure subscribing to ebs
will be synchronous and you may end up always returning null from the inner function. Since you depend on another Observable, you could just simply compose it through:
public rx.Observable<Game> findGame( long templateId, GameModelType game_model, GameStateType state) { return context.findGame(templateId, state) .flatMap(gameRawReply -> { switch(game_model) { case SINGLE: { return ebs.map(t1 -> { Game game; if (!singleGame.contains(0) { game = new Game(); singleGames.put(0, game); } else { game = singleGames.get(0); } return game; }); } } return rx.Observable.just(null); }); }