为什么给每个Actor绑定的InputListener触发时返回的坐标均相同?
问题根因
你遇到的是Java匿名内部类捕获局部变量的经典问题,核心原因如下:
- 如果你的
targetField数组实际定义在双重for循环的外部,所有匿名InputListener实例捕获的都是同一个数组对象的引用。每次循环修改的都是这个数组的内容,循环结束后数组的值就是最后一次迭代的[5,5],所有回调触发时读取的都是这个数组最终的内容。 - 哪怕你是按贴出的代码把
targetField定义在循环内部,也存在隐患:数组是引用类型,只要任何地方修改了对应数组的内容,回调输出结果都会受影响。
修复方案
直接使用两个不可变的基本类型变量存储当前循环的坐标,避免引用类型带来的内容篡改问题,修改后代码如下:
for (int i = 0; i < mapSize[0]; i++) { for (int j = 0; j < mapSize[1]; j++) { hashCodes[i][j] = new Image(emptyField); // 用final修饰的基本类型存储坐标,匿名类捕获的是固定值而非引用 final int targetI = i; final int targetJ = j; hashCodes[i][j].addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { return true; } @Override public void touchUp(InputEvent event, float x, float y, int pointer, int button) { int[] targetField = new int[]{targetI, targetJ}; System.out.println("Empty Field clicked" + Arrays.toString(targetField)); for (Image[] imageRow : hashCodes) { for (Image actualImage : imageRow) { System.out.print(actualImage.hashCode() + " "); } System.out.println(); } if (MarvelousMashup.useInfinityStone) { MarvelousMashup.useInfinityStone = false; MainMenu.client.getMessageHandler().sendUseInfinityStoneRequest (MarvelousMashup.activeCharacter, getActualEntity(MarvelousMashup.activeCharacter).getPosition(), targetField, getStone(MarvelousMashup.useInfinityStoneID)); } } }); tempField.addActor(hashCodes[i][j]); } }
原理说明
- Java中匿名内部类访问外部局部变量时,要求变量是
final或effectively final(赋值后不再修改)。 - 基本类型变量被捕获时,匿名类会存储该变量的值拷贝,后续外部变量的修改不会影响拷贝值。
- 引用类型变量被捕获时,匿名类存储的是引用的拷贝,引用指向的对象内容修改时,所有持有该引用的地方都会读到最新值。
内容的提问来源于stack exchange,提问作者Biohazard
相关产品推荐
相关产品推荐

