2

Does Java have anything similar to C#'s Action type? Is Java 8 or Pre-Java 8 the way to go? Why or why not? I'm trying to avoid going down any rabbit holes. Please help me understand my options...

Statement:

Driver.NoWait(() => links = rowFindElements(ByLinkText(title)));

Methods:

 public static void NoWait(Action action)
 {
      TurnOffWait();
      action();
      TurnOnWait();

 }

 public static void TurnOnWait()
 {
      Instance.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(5));
 }

 public static void TurnOffWait()
 {
      Instance.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(0));
 }

UPDATE

Thanks to @Nick Y and a programmer at the office (who told me the history of Java pragmatics vs Java traditionalists). This is the outcome of my findings:

Feature Menu Class 1st Way Post Java 8

public class FeatureMenu 
{
    static WebElement sideTab;


    public static void Expand() 
    {
        try
        {
            Iframe.Default.SwitchTo();

            Driver.NoWait(() -> sideTab = Driver.Instance.findElement(By.cssSelector("div.wijmo-wijsplitter-v-panel1-collapsed")));

            sideTab.click();
            Log.Info("Feature Menu Expanded.");
        }
        catch(Exception e)
        {
            Log.Error("[EXCEPTION CAUGHT] : FeatureMenu.Expand()");
            throw(e);
        }
    }
}

Feature Menu 2nd Way Pre Java 8

public class FeatureMenu 
{
    static WebElement sideTab;
    public static void Expand() 
    {
        try
        {
            Iframe.Default.SwitchTo();

            Driver.NoWait( new Driver.Action(){ public void apply(){
            sideTab = Driver.Instance.findElement(By.cssSelector("div.wijmo-wijsplitter-v-panel1-collapsed"));
                                        }
        });

            sideTab.click();
            Log.Info("Feature Menu Expanded.");
        }
        catch(Exception e)
        {
            Log.Error("[EXCEPTION CAUGHT] : FeatureMenu.Expand()");
            throw(e);
        }
    }
}

Driver Class that can be used with either approach

public class Driver
{

    public static WebDriver Instance;

    public static String BaseAddress(String baseAddress)
    {   
        return baseAddress;
    }

    public static void Initialize(String driverType)
    {
        Instance = new FirefoxDriver();

        Instance.manage().window().maximize();

        TurnOnWait();
    }

    @FunctionalInterface
    public interface Action {
        void apply();

    }

    public static void NoWait(Action action)
    {
        TurnOffWait();
        action.apply();
        TurnOnWait();
    }

    public static void TurnOffWait()
    {
        Instance.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS);
    }

    public static void TurnOnWait()
    {
        Instance.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
    }
}
phil o.O
  • 406
  • 2
  • 4
  • 20
  • Try something like Runnable: https://docs.oracle.com/javase/7/docs/api/java/lang/Runnable.html – Mikey Mar 24 '16 at 15:19
  • Correct me if I am wrong, however based on the documentation I don't think this would be efficient for what I am trying to accomplish. – phil o.O Mar 24 '16 at 18:28

2 Answers2

1

If you want to get closer to the most recent C# version you would want to use Java 8 (not pre-Java 8)

Java 8 has lambdas and functional interfaces which can get you very close to how things are done in C#. Google "functional interface java". There is a lot good information out there.

In the context of your specific question think about functional interfaces in java as delegates in C#.

public delegate void Action()

can be mimicked in java 8 as

@FunctionalInterface
public interface Action() {
    void apply();
}

With this in mind, here is the simple usage of Action interface and lambda

public class MainWithAction {

    public static void main(String[] args) {
        noWait(() -> doSomething());
    }

    public static void noWait(Action action) {
        turnOffWait();
        action.apply();
        turnOnWait();
    }

    public static void doSomething() { /* do something */ }
    public static void turnOnWait() { /* skipped */ }
    public static void turnOffWait() { /* skipped */ }
}

It is not a requirement to use @FunctionalInterface annotation but it helps compiler to generate error messages in certain cases. apply() method name can be changed to anything else it is more of a convention thing.

Java 8 has a few predefined functional interfaces in package java.util.function however it appears that there is nothing that returns void and takes no parameters so you would need to have your own. Read more here: https://softwareengineering.stackexchange.com/questions/276859/what-is-the-name-of-a-function-that-takes-no-argument-and-returns-nothing

You may want to consider having NoWaitAction interface which can be a more appropriate name for your scenario instead of a generic Action interface. It's up to you.

Having said all that I am moving to more interesting point of going down the rabbit hole.

Your particular use case may not map 100% into the java code. Let's try to convert this line.

Driver.NoWait(() => links = rowFindElements(ByLinkText(title)));

What caught my eye here is the links variable. It does look like a local variable to me. If this is not the case then the bellow is irrelevant, but may still trigger some thoughts.

For the sake of this exercise I am going to assume that links is a local variable of List of Strings type and rowFindElements takes String parameter and returns a List of Strings

Here is one way of converting this into java (with NoWaitAction as an example of my above point):

@FunctionalInterface
public interface NoWaitAction {
    void apply();
}

and the meat

public class MainNoWaitAction {

   public static void main(String[] args) {
       List<String> links = new ArrayList<>();
       String title = "title";

       noWait(() -> links.addAll(rowFindElements(title)));
   }

   public static void noWait(NoWaitAction action) {
       turnOffWait();
       action.apply();
       turnOnWait();
   }

   public static void turnOnWait() { /* skipped */ }
   public static void turnOffWait() { /* skipped */ }

   public static List<String> rowFindElements(String title) {
       return new ArrayList<>(); // populate the list
   }
}

There are various other ways of doing it, but the main point here is that the following will not compile

    noWait(() -> links = rowFindElements(title));

Why? Read this answer for example https://stackoverflow.com/a/4732617/5947137

Update 1

Based on OP comments I would like to suggest another approach

public class MainNoWaitAction {

    public static void main(String[] args) {
        List<String> links;
        Object otherVariable;

        String title = "title";

        links = noWait(() -> rowFindElements(title));
        otherVariable = noWait(() -> createAnObject());
    }

    public static <T> T noWait(Supplier<T> supplier) {
        turnOffWait();
        try {
            return supplier.get();
        } finally {
            turnOnWait();
        }
    }

    private static void turnOnWait() { /* skipped */ }
    private static void turnOffWait() { /* skipped */ }

    private static List<String> rowFindElements(String title) {
        return new ArrayList<>(); // populate the list
    }

    private static Object createAnObject() {
        return new Object();
    } 
}
Community
  • 1
  • 1
Nick Y
  • 172
  • 1
  • 5
  • Nick thank you for the information and resources. I can confirm `noWait(() -> links = rowFindElements(title));` does not compile. I'll keep you updated with my findings. – phil o.O Mar 24 '16 at 18:44
  • I followed your comment, however I am having difficulty with the following statement `Driver.NoWait(() -> sideTab = Driver.Instance.findElement(By.cssSelector("div.wijmo-wijsplitter-v-panel1-collapsed")));` Also, I'm still reading the resources you supplied. – phil o.O Mar 24 '16 at 18:57
  • @PhiL, you are welcome. Additionally depending on your requirements if you are not constrained with using java as a language take a look at [Kotlin](https://kotlinlang.org/). This is one of the newer jvm based languages that compiles into java byte code and runs in java vm but feels very much like C# to program in. – Nick Y Mar 24 '16 at 18:57
  • @PhiL, it's hard to suggest something good without seeing the big picture. If you make sideTab a class variable (instead of local variable) it will compile and work, but may not be the best in terms of structuring your code. – Nick Y Mar 24 '16 at 19:06
  • sideTab is of the WebElement class. I'll keep you updated. – phil o.O Mar 24 '16 at 19:08
  • I am very much shooting in the dark here, but I posted another way as an update. – Nick Y Mar 24 '16 at 19:28
  • thanks. I updated the question. Feel free to comment. – phil o.O Mar 24 '16 at 19:50
0

Yes, you can write code the same way in Java :

public interface Action {
    void apply();
}

public static void DoSomething(Action action)
{
    action.apply();
}

public static void main(String[] args) throws IOException {

    DoSomething(() -> System.out.println("test action"));

}
Florent B.
  • 41,537
  • 7
  • 86
  • 101
  • I had upgrade my project to 1.8 in order to use this. I'm currently integrating it with my framework. I'll update this thread with my findings. – phil o.O Mar 24 '16 at 18:27
  • This approach is throwing an error: `Local Variable defined in an enclosing scope must be final or effectively final ` – phil o.O Mar 24 '16 at 18:42