diff --git a/src/main/java/dev/latvian/mods/rhino/JavaAdapter.java b/src/main/java/dev/latvian/mods/rhino/JavaAdapter.java
index 50bd0d4..7517ea0 100644
--- a/src/main/java/dev/latvian/mods/rhino/JavaAdapter.java
+++ b/src/main/java/dev/latvian/mods/rhino/JavaAdapter.java
@@ -18,6 +18,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.ArrayDeque;
 import java.util.Map;
 
 public final class JavaAdapter implements IdFunctionCall {
@@ -359,16 +360,29 @@ public static byte[] createAdapterCode(ObjToIntMap functionNames, String adapter
 	static Method[] getOverridableMethods(Class<?> clazz) {
 		ArrayList<Method> list = new ArrayList<>();
 		HashSet<String> skip = new HashSet<>();
+		ArrayDeque<Class<?>> interfaces = new ArrayDeque<>();
+		HashSet<Class<?>> visitedInterfaces = new HashSet<>();
 		// Check superclasses before interfaces so we always choose
 		// implemented methods over abstract ones, even if a subclass
 		// re-implements an interface already implemented in a superclass
 		// (e.g. java.util.ArrayList)
 		for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {
 			appendOverridableMethods(c, list, skip);
-		}
-		for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {
 			for (Class<?> intf : c.getInterfaces()) {
-				appendOverridableMethods(intf, list, skip);
+				interfaces.add(intf);
+			}
+		}
+		// Visit interfaces in depth first order.
+		while (!interfaces.isEmpty()) {
+			var intf = interfaces.remove();
+			if (visitedInterfaces.contains(intf)) {
+				continue;
+			}
+			visitedInterfaces.add(intf);
+			appendOverridableMethods(intf, list, skip);
+			var subIntf = intf.getInterfaces();
+			for (int j = subIntf.length -1; j >= 0; j--) {
+				interfaces.addFirst(subIntf[j]);
 			}
 		}
 		return list.toArray(new Method[0]);