Merge "Make minification deterministic."
diff --git a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
index ddb0244..5755c04 100644
--- a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
+++ b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
@@ -33,6 +33,10 @@
"}"
);
+ public CompatProguardCommandBuilder() {
+ this(true);
+ }
+
public CompatProguardCommandBuilder(boolean forceProguardCompatibility) {
super(forceProguardCompatibility);
setIgnoreDexInArchive(true);
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index ddb4b24..188f6d1 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "v0.2.10-dev";
+ public static final String LABEL = "v0.2.11-dev";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
index c7fc65d..09d6696 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
@@ -5,14 +5,13 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
-import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.objects.Reference2IntArrayMap;
import it.unimi.dsi.fastutil.objects.Reference2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
+import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -91,14 +90,59 @@
return map;
}
+ /**
+ * Here, 'depth' of a program class is an integer one bigger then the maximum depth of its
+ * superclass and implemented interfaces. The depth of classes without any or without known
+ * superclasses and interfaces is 1.
+ */
+ private static class ProgramClassDepthsMemoized {
+ private final DexApplication application;
+ private final Reference2IntMap<DexProgramClass> depthOfClasses = new Reference2IntArrayMap<>();
+
+ ProgramClassDepthsMemoized(DexApplication application) {
+ this.application = application;
+ }
+
+ int getDepth(DexProgramClass programClass) {
+ return depthOfClasses.computeIfAbsent(
+ programClass,
+ programClassToCompute -> {
+ // Emulating the algorithm of com.android.dx.merge.SortableType.tryAssignDepth().
+ DexType superType = programClassToCompute.superType;
+ int maxDepth;
+ if (superType == null) {
+ maxDepth = 0;
+ } else {
+ maxDepth = 1;
+ DexProgramClass superClass = application.programDefinitionFor(superType);
+ if (superClass != null) {
+ maxDepth = getDepth(superClass);
+ }
+ }
+ for (DexType inf : programClassToCompute.interfaces.values) {
+ DexProgramClass infClass = application.programDefinitionFor(inf);
+ maxDepth = Math.max(maxDepth, infClass == null ? 1 : getDepth(infClass));
+ }
+ return maxDepth + 1;
+ });
+ }
+ }
+
private static DexProgramClass[] sortClasses(DexApplication application,
Collection<DexProgramClass> classes) {
- SortingProgramClassVisitor classVisitor = new SortingProgramClassVisitor(application, classes);
// Collect classes in subtyping order, based on a sorted list of classes to start with.
- classVisitor.run(
- classes.stream().sorted(Comparator.comparing(DexClass::getType))
- .collect(Collectors.toList()));
- return classVisitor.getSortedClasses();
+ ProgramClassDepthsMemoized classDepths = new ProgramClassDepthsMemoized(application);
+ List<DexProgramClass> sortedClasses =
+ classes
+ .stream()
+ .sorted(
+ (x, y) -> {
+ int dx = classDepths.getDepth(x);
+ int dy = classDepths.getDepth(y);
+ return dx != dy ? dx - dy : x.type.compareTo(y.type);
+ })
+ .collect(Collectors.toList());
+ return sortedClasses.toArray(new DexProgramClass[sortedClasses.size()]);
}
private static <T> Collection<T> keysOrEmpty(Map<T, ?> map) {
@@ -179,34 +223,4 @@
public int getOffsetFor(DexMethodHandle methodHandle) {
return getOffsetFor(methodHandle, methodHandles);
}
-
- private static class SortingProgramClassVisitor extends ProgramClassVisitor {
- private final Set<DexClass> classSet = Sets.newIdentityHashSet();
- private final DexProgramClass[] sortedClasses;
-
- private int index = 0;
-
- SortingProgramClassVisitor(DexApplication application,
- Collection<DexProgramClass> classes) {
- super(application);
- this.sortedClasses = new DexProgramClass[classes.size()];
- classSet.addAll(classes);
- }
-
- @Override
- public void visit(DexType type) {}
-
- @Override
- public void visit(DexClass clazz) {
- if (classSet.contains(clazz)) {
- assert index < sortedClasses.length;
- sortedClasses[index++] = (DexProgramClass) clazz;
- }
- }
-
- DexProgramClass[] getSortedClasses() {
- assert index == sortedClasses.length;
- return sortedClasses;
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 9789c44..870521d 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -536,6 +536,15 @@
Log.verbose(getClass(), "Method `%s` is targeted.", encodedMethod.method);
}
targetedMethods.add(encodedMethod, reason);
+ if (options.forceProguardCompatibility) {
+ // Keep targeted default methods in compatibility mode. The tree pruner will otherwise make
+ // these methods abstract, whereas Proguard does not (seem to) touch their code.
+ DexClass clazz = appInfo.definitionFor(encodedMethod.method.holder);
+ if (!encodedMethod.accessFlags.isAbstract()
+ && clazz.isInterface() && !clazz.isLibraryClass()) {
+ markMethodAsKeptWithCompatRule(encodedMethod);
+ }
+ }
}
/**
@@ -1179,6 +1188,14 @@
}
}
+ private void markMethodAsKeptWithCompatRule(DexEncodedMethod method) {
+ ProguardKeepRule rule =
+ ProguardConfigurationUtils.buildDefaultMethodKeepRule(
+ appInfo.definitionFor(method.method.holder), method);
+ proguardCompatibilityWorkList.add(
+ Action.markMethodLive(method, KeepReason.dueToProguardCompatibilityKeepRule(rule)));
+ }
+
private void handleProguardReflectiveBehavior(DexEncodedMethod method) {
try {
IRCode code = method.buildIR(options);
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index 9fea981..08610aa 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -5,7 +5,11 @@
package com.android.tools.r8.shaking;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
public class ProguardConfigurationUtils {
public static ProguardKeepRule buildDefaultInitializerKeepRule(DexClass clazz) {
@@ -27,4 +31,27 @@
}
return builder.build();
}
+
+ public static ProguardKeepRule buildDefaultMethodKeepRule(
+ DexClass clazz, DexEncodedMethod method) {
+ assert clazz.type == method.method.holder;
+ ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
+ builder.setType(ProguardKeepRuleType.KEEP);
+ builder.getModifiersBuilder().allowsObfuscation = true;
+ builder.getModifiersBuilder().allowsOptimization = true;
+ builder.getClassAccessFlags().setPublic();
+ builder.setClassType(ProguardClassType.INTERFACE);
+ builder.setClassNames(
+ ProguardClassNameList.singletonList(ProguardTypeMatcher.create(clazz.type)));
+ ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
+ memberRuleBuilder.setRuleType(ProguardMemberType.METHOD);
+ memberRuleBuilder.setName(method.method.name.toString());
+ memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(method.method.proto.returnType));
+ List<ProguardTypeMatcher> arguments = Arrays.stream(method.method.proto.parameters.values)
+ .map(ProguardTypeMatcher::create)
+ .collect(Collectors.toList());
+ memberRuleBuilder.setArguments(arguments);
+ builder.getMemberRules().add(memberRuleBuilder.build());
+ return builder.build();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/shaking/defaultmethods/ClassImplementingInterface.java b/src/test/java/com/android/tools/r8/shaking/defaultmethods/ClassImplementingInterface.java
new file mode 100644
index 0000000..b8261ad
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/defaultmethods/ClassImplementingInterface.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.defaultmethods;
+
+public class ClassImplementingInterface implements InterfaceWithDefaultMethods {
+ public int method() {
+ return 41;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java b/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
index 4fac23b..3de225d 100644
--- a/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.invokesuper.Consumer;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.DexInspector.ClassSubject;
@@ -26,9 +27,10 @@
throws Exception {
R8Command.Builder builder = R8Command.builder();
builder.addProgramFiles(ToolHelper.getClassFileForTestClass(InterfaceWithDefaultMethods.class));
+ builder.addProgramFiles(ToolHelper.getClassFileForTestClass(ClassImplementingInterface.class));
builder.addProgramFiles(ToolHelper.getClassFileForTestClass(TestClass.class));
builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
- builder.setMinApiLevel(26);
+ builder.setMinApiLevel(AndroidApiLevel.O.getLevel());
// Always keep main in the test class, so the output never becomes empty.
builder.addProguardConfiguration(ImmutableList.of(
"-keep class " + TestClass.class.getCanonicalName() + "{",
@@ -59,6 +61,14 @@
assertFalse(method.isAbstract());
}
+ private void defaultMethodAbstract(DexInspector inspector) {
+ ClassSubject clazz = inspector.clazz(InterfaceWithDefaultMethods.class);
+ assertTrue(clazz.isPresent());
+ MethodSubject method = clazz.method("int", "method", ImmutableList.of());
+ assertTrue(method.isPresent());
+ assertTrue(method.isAbstract());
+ }
+
@Test
public void test() throws Exception {
runTest(ImmutableList.of(), this::interfaceNotKept);
@@ -76,5 +86,18 @@
" public int method();",
"}"
), this::defaultMethodKept);
+ runTest(ImmutableList.of(
+ "-keep class " + ClassImplementingInterface.class.getCanonicalName() + "{",
+ " <methods>;",
+ "}"
+ ), this::defaultMethodNotKept);
+ runTest(ImmutableList.of(
+ "-keep class " + ClassImplementingInterface.class.getCanonicalName() + "{",
+ " <methods>;",
+ "}",
+ "-keep class " + TestClass.class.getCanonicalName() + "{",
+ " public void useInterfaceMethod();",
+ "}"
+ ), this::defaultMethodAbstract);
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/defaultmethods/TestClass.java b/src/test/java/com/android/tools/r8/shaking/defaultmethods/TestClass.java
index fcb240a..5e46681 100644
--- a/src/test/java/com/android/tools/r8/shaking/defaultmethods/TestClass.java
+++ b/src/test/java/com/android/tools/r8/shaking/defaultmethods/TestClass.java
@@ -6,6 +6,11 @@
public class TestClass {
+ public void useInterfaceMethod() {
+ InterfaceWithDefaultMethods iface = new ClassImplementingInterface();
+ System.out.println(iface.method());
+ }
+
public static void main(String[] args) {
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index 9679970..085e46c 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -16,15 +16,21 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.invokesuper.Consumer;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.ProguardClassNameList;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardConfigurationParser;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.shaking.ProguardMemberRule;
import com.android.tools.r8.shaking.ProguardMemberType;
+import com.android.tools.r8.shaking.forceproguardcompatibility.defaultmethods.ClassImplementingInterface;
+import com.android.tools.r8.shaking.forceproguardcompatibility.defaultmethods.InterfaceWithDefaultMethods;
+import com.android.tools.r8.shaking.forceproguardcompatibility.defaultmethods.TestClass;
import com.android.tools.r8.shaking.forceproguardcompatibility.keepattributes.TestKeepAttributes;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
import com.android.tools.r8.utils.DexInspector;
@@ -373,4 +379,115 @@
testKeepAttributes(false, false, true);
testKeepAttributes(false, true, true);
}
+
+ private void runKeepDefaultMethodsTest(
+ List<String> additionalKeepRules,
+ Consumer<DexInspector> inspection,
+ Consumer<ProguardConfiguration> compatInspection) throws Exception {
+ Class mainClass = TestClass.class;
+ CompatProguardCommandBuilder builder = new CompatProguardCommandBuilder();
+ builder.addProgramFiles(ToolHelper.getClassFilesForTestPackage(mainClass.getPackage()));
+ builder.addProguardConfiguration(ImmutableList.of(
+ "-keep class " + mainClass.getCanonicalName() + "{",
+ " public <init>();",
+ " public static void main(java.lang.String[]);",
+ "}",
+ "-dontobfuscate"),
+ Origin.unknown());
+ builder.addProguardConfiguration(additionalKeepRules, Origin.unknown());
+ builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+ builder.setMinApiLevel(AndroidApiLevel.O.getLevel());
+ Path proguardCompatibilityRules = temp.newFile().toPath();
+ builder.setProguardCompatibilityRulesOutput(proguardCompatibilityRules);
+ AndroidApp app = ToolHelper.runR8(builder.build());
+ inspection.accept(new DexInspector(app));
+ // Check the Proguard compatibility configuration generated.
+ ProguardConfigurationParser parser =
+ new ProguardConfigurationParser(new DexItemFactory(),
+ new Reporter(new DefaultDiagnosticsHandler()));
+ parser.parse(proguardCompatibilityRules);
+ ProguardConfiguration configuration = parser.getConfigRawForTesting();
+ compatInspection.accept(configuration);
+ }
+
+ private void noCompatibilityRules(ProguardConfiguration configuration) {
+ assertEquals(0, configuration.getRules().size());
+ }
+
+ private void defaultMethodKept(DexInspector inspector) {
+ ClassSubject clazz = inspector.clazz(InterfaceWithDefaultMethods.class);
+ assertTrue(clazz.isPresent());
+ MethodSubject method = clazz.method("int", "method", ImmutableList.of());
+ assertTrue(method.isPresent());
+ assertFalse(method.isAbstract());
+ }
+
+ private void defaultMethodCompatibilityRules(ProguardConfiguration configuration) {
+ assertEquals(1, configuration.getRules().size());
+ ProguardConfigurationRule rule = configuration.getRules().get(0);
+ Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+ ProguardClassNameList classNames = rule.getClassNames();
+ assertEquals(1, classNames.size());
+ DexType type = classNames.asSpecificDexTypes().get(0);
+ assertEquals(type.toSourceString(), InterfaceWithDefaultMethods.class.getCanonicalName());
+ assertEquals(1, memberRules.size());
+ ProguardMemberRule memberRule = memberRules.iterator().next();
+ assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType());
+ assertTrue(memberRule.getName().matches("method"));
+ assertTrue(memberRule.getType().matches(configuration.getDexItemFactory().intType));
+ assertEquals(0, memberRule.getArguments().size());
+ }
+
+ private void defaultMethod2Kept(DexInspector inspector) {
+ ClassSubject clazz = inspector.clazz(InterfaceWithDefaultMethods.class);
+ assertTrue(clazz.isPresent());
+ MethodSubject method =
+ clazz.method("void", "method2", ImmutableList.of("java.lang.String", "int"));
+ assertTrue(method.isPresent());
+ assertFalse(method.isAbstract());
+ }
+
+ private void defaultMethod2CompatibilityRules(ProguardConfiguration configuration) {
+ assertEquals(1, configuration.getRules().size());
+ ProguardConfigurationRule rule = configuration.getRules().get(0);
+ Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+ ProguardClassNameList classNames = rule.getClassNames();
+ assertEquals(1, classNames.size());
+ DexType type = classNames.asSpecificDexTypes().get(0);
+ assertEquals(type.toSourceString(), InterfaceWithDefaultMethods.class.getCanonicalName());
+ assertEquals(1, memberRules.size());
+ ProguardMemberRule memberRule = memberRules.iterator().next();
+ assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType());
+ assertTrue(memberRule.getName().matches("method2"));
+ assertTrue(memberRule.getType().matches(configuration.getDexItemFactory().voidType));
+ assertEquals(2, memberRule.getArguments().size());
+ assertTrue(
+ memberRule.getArguments().get(0).matches(configuration.getDexItemFactory().stringType));
+ assertTrue(memberRule.getArguments().get(1).matches(configuration.getDexItemFactory().intType));
+ }
+
+ @Test
+ public void keepDefaultMethodsTest() throws Exception {
+ runKeepDefaultMethodsTest(ImmutableList.of(
+ "-keep interface " + InterfaceWithDefaultMethods.class.getCanonicalName() + "{",
+ " public int method();",
+ "}"
+ ), this::defaultMethodKept, this::noCompatibilityRules);
+ runKeepDefaultMethodsTest(ImmutableList.of(
+ "-keep class " + ClassImplementingInterface.class.getCanonicalName() + "{",
+ " <methods>;",
+ "}",
+ "-keep class " + TestClass.class.getCanonicalName() + "{",
+ " public void useInterfaceMethod();",
+ "}"
+ ), this::defaultMethodKept, this::defaultMethodCompatibilityRules);
+ runKeepDefaultMethodsTest(ImmutableList.of(
+ "-keep class " + ClassImplementingInterface.class.getCanonicalName() + "{",
+ " <methods>;",
+ "}",
+ "-keep class " + TestClass.class.getCanonicalName() + "{",
+ " public void useInterfaceMethod2();",
+ "}"
+ ), this::defaultMethod2Kept, this::defaultMethod2CompatibilityRules);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultmethods/ClassImplementingInterface.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultmethods/ClassImplementingInterface.java
new file mode 100644
index 0000000..9b5adfe
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultmethods/ClassImplementingInterface.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.forceproguardcompatibility.defaultmethods;
+
+public class ClassImplementingInterface implements InterfaceWithDefaultMethods {
+ public int method() {
+ return 41;
+ }
+ public void method2(String x, int y) {
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultmethods/InterfaceWithDefaultMethods.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultmethods/InterfaceWithDefaultMethods.java
new file mode 100644
index 0000000..d7503d6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultmethods/InterfaceWithDefaultMethods.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.forceproguardcompatibility.defaultmethods;
+
+public interface InterfaceWithDefaultMethods {
+ default int method() {
+ return 42;
+ }
+ default void method2(String x, int y) {
+ System.out.println(y + x);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultmethods/TestClass.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultmethods/TestClass.java
new file mode 100644
index 0000000..35ba3c5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultmethods/TestClass.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.forceproguardcompatibility.defaultmethods;
+
+public class TestClass {
+
+ public void useInterfaceMethod() {
+ InterfaceWithDefaultMethods iface = new ClassImplementingInterface();
+ System.out.println(iface.method());
+ }
+
+ public void useInterfaceMethod2() {
+ InterfaceWithDefaultMethods iface = new ClassImplementingInterface();
+ iface.method2("a", 1);
+ }
+
+ public static void main(String[] args) {
+ }
+}