I am having trouble with the Appium java client because it seems they did weird things with their project.
Basically, they are using Selenium in their project which should work just fine but they copied one package from Selenium partly to their project (org.openqa.selenium) and made some small adaptions to the classes inside. Basically, they added generics to the interfaces. Now we have duplicate classes in the same package in different libraries which of course leads to problems.
I created a simple Gradle project to demonstrate that. Following my build.gradle:
plugins {
id 'java-library'
}
dependencies {
api 'io.appium:java-client:6.1.0'
}
repositories {
jcenter()
}
And my class Interactions.java:
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
public class Interactions {
public static void touchWebElement(By by, WebDriver driver) {
touchWebElement(driver.findElement(by), driver);
}
public static void touchWebElement(WebElement element, WebDriver driver) {
// DO SOMETHING
}
}
Now if I compile that project I get the following error:
The method touchWebElement(By, WebDriver) is ambiguous for the type Interactions Interactions.java line 8
I think it is ambiguous because the interface WebElement exists twice.
How can I fix that problem?
Solution:
With help of the accepted answer I was able to fix those issues. Although I needed to come up with a slightly different solution. The problem I faced was that classes which called my Interactions.java needed those casts too which would lead in 1000+ adaptions. To prevent this I changed my methods to take Object as parameter:
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
public class Interactions {
public static void touchWebElement(Object object, WebDriver driver){
WebElement webElement = castObjectToWebElement(element, driver);
//DO SOMETHING
}
private static WebElement castObjectToWebElement(Object object, WebDriver driver) {
if (object instanceof WebElement) {
return (WebElement) object;
} else if (object instanceof By) {
return driver.findElement((By) object);
}
throw new IllegalArgumentException("Invalid type");
}
}
It might not be an optimal solution but it works and won't need changes in all our other classes and everyone can work with those Interaction methods as until now.
The problem are not duplicate classes but the way generics are used. Here is a little MCVE replicating the situation in Appium's WebDriver
class:
package de.scrum_master.stackoverflow;
public interface WebElement {}
package de.scrum_master.stackoverflow;
public interface WebDriver {
<T extends WebElement> T findElement();
}
package de.scrum_master.stackoverflow;
public class Application {
static WebDriver webDriver;
static void myOverloadedMethod(String text) {}
static void myOverloadedMethod(WebElement text) {}
public static void main(String[] args) {
// These 3 variants work
myOverloadedMethod("test");
myOverloadedMethod((WebElement) webDriver.findElement());
WebElement webElement = webDriver.findElement();
myOverloadedMethod(webElement);
// This one does not work
myOverloadedMethod(webDriver.findElement());
}
}
Explanation: Due to type erasure doSomething
's generic return type <T extends WebElement>
evaluates to Object
, so when trying to use that result for calling myOverloadedMethod(..)
the compiler does not know which method to select.
Solution: You need to help by casting or explicitly declaring a type for a variable containing the method parameter.
P.S.: If you would modify the interface definition from interface WebDriver
to interface WebDriver<T>
, the compilation error would go away. But Appium's implementation does not do that, maybe because they want to stay as compatible(?) as possible to the original Selenium class. You have to ask them.
Update: Because the OP seems to have problems understanding my answer or to believe I did not try his sample code, which of course I used to reproduce and understand his problem:
package de.scrum_master.appium;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
public class Interactions {
public static void touchWebElement(WebElement element, WebDriver driver) {}
public static void touchWebElement(By by, WebDriver driver) {
// Works
WebElement webElement = driver.findElement(by);
touchWebElement(webElement, driver);
// Works
touchWebElement((WebElement) driver.findElement(by), driver);
// Ambiguous due to type erasure -> does not work
touchWebElement(driver.findElement(by), driver);
}
}
There is absolutely no need rename methods, re-package any classes or perform other types of Maven/Gradle stunts here.
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