diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithFindFirstFix.java b/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithFindFirstFix.java index 9aafee7410ba..848a3089a6c8 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithFindFirstFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithFindFirstFix.java @@ -44,6 +44,7 @@ class ReplaceWithFindFirstFix extends MigrateToStreamFix { @NotNull List operations) { PsiStatement statement = tb.getSingleStatement(); PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); + String stream = generateStream(iteratedValue, operations).append(".findFirst()").toString(); if (statement instanceof PsiReturnStatement) { PsiReturnStatement returnStatement = (PsiReturnStatement)statement; PsiExpression value = returnStatement.getReturnValue(); @@ -52,14 +53,10 @@ class ReplaceWithFindFirstFix extends MigrateToStreamFix { if (nextReturnStatement == null) return; PsiExpression orElseExpression = nextReturnStatement.getReturnValue(); if (!ExpressionUtils.isSimpleExpression(orElseExpression)) return; - StringBuilder builder = generateStream(iteratedValue, operations).append(".findFirst()"); - if (!(value instanceof PsiReferenceExpression) || ((PsiReferenceExpression)value).resolve() != tb.getVariable()) { - builder.append(".map(").append(tb.getVariable().getName()).append(" -> ").append(value.getText()).append(")"); - } - builder.append(".orElse(").append(orElseExpression.getText()).append(")"); + stream = generateOptionalUnwrap(stream, tb, value, orElseExpression); restoreComments(foreachStatement, body); boolean siblings = nextReturnStatement.getParent() == foreachStatement.getParent(); - PsiElement result = foreachStatement.replace(elementFactory.createStatementFromText("return " + builder + ";", foreachStatement)); + PsiElement result = foreachStatement.replace(elementFactory.createStatementFromText("return " + stream + ";", foreachStatement)); if (siblings) { nextReturnStatement.delete(); } @@ -77,24 +74,38 @@ class ReplaceWithFindFirstFix extends MigrateToStreamFix { PsiVariable var = (PsiVariable)element; PsiExpression value = assignment.getRExpression(); if (value == null) return; - StringBuilder builder = generateStream(iteratedValue, operations).append(".findFirst()"); - if (!(value instanceof PsiReferenceExpression) || ((PsiReferenceExpression)value).resolve() != tb.getVariable()) { - builder.append(".map(").append(tb.getVariable().getName()).append(" -> ").append(value.getText()).append(")"); - } restoreComments(foreachStatement, body); if (StreamApiMigrationInspection.isDeclarationJustBefore(var, foreachStatement)) { PsiExpression initializer = var.getInitializer(); if (initializer != null) { PsiElement result = - initializer.replace(elementFactory.createExpressionFromText(builder + ".orElse(" + initializer.getText() + ")", initializer)); + initializer.replace(elementFactory.createExpressionFromText(generateOptionalUnwrap(stream, tb, value, initializer), initializer)); removeLoop(foreachStatement); simplifyAndFormat(project, result); return; } } - PsiElement result = foreachStatement.replace( - elementFactory.createStatementFromText(var.getName() + " = " + builder + ".orElse(" + var.getName() + ");", foreachStatement)); + PsiElement result = foreachStatement.replace(elementFactory.createStatementFromText( + var.getName() + " = " + generateOptionalUnwrap(stream, tb, value, lValue) + ";", foreachStatement)); simplifyAndFormat(project, result); } } + + private static String generateOptionalUnwrap(String stream, @NotNull StreamApiMigrationInspection.TerminalBlock tb, + PsiExpression trueExpression, PsiExpression falseExpression) { + PsiVariable var = tb.getVariable(); + if (!StreamApiMigrationInspection.isIdentityMapping(var, trueExpression)) { + if(trueExpression instanceof PsiTypeCastExpression && ExpressionUtils.isNullLiteral(falseExpression)) { + PsiTypeCastExpression castExpression = (PsiTypeCastExpression)trueExpression; + PsiTypeElement castType = castExpression.getCastType(); + // pull cast outside to avoid the .map() step + if(castType != null && StreamApiMigrationInspection.isIdentityMapping(var, castExpression.getOperand())) { + return "(" + castType.getText() + ")" + stream + ".orElse(null)"; + } + } + stream += ".map(" + StreamApiMigrationInspection.createLambda(var, trueExpression) + ")"; + } + stream += ".orElse(" + falseExpression.getText() + ")"; + return stream; + } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithMatchFix.java b/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithMatchFix.java index 42e754ff4df1..45eebe674d98 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithMatchFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithMatchFix.java @@ -21,6 +21,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.siyeh.ig.psiutils.BoolUtils; +import com.siyeh.ig.psiutils.ExpressionUtils; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -51,31 +52,66 @@ class ReplaceWithMatchFix extends MigrateToStreamFix { @NotNull PsiStatement body, @NotNull StreamApiMigrationInspection.TerminalBlock tb, @NotNull List operations) { - PsiReturnStatement returnStatement = (PsiReturnStatement)tb.getSingleStatement(); - PsiExpression value = returnStatement.getReturnValue(); PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); - restoreComments(foreachStatement, body); - if (StreamApiMigrationInspection.isLiteral(value, Boolean.TRUE) || StreamApiMigrationInspection.isLiteral(value, Boolean.FALSE)) { - boolean foundResult = (boolean)((PsiLiteralExpression)value).getValue(); - PsiReturnStatement nextReturnStatement = StreamApiMigrationInspection.getNextReturnStatement(foreachStatement); - if (nextReturnStatement != null && StreamApiMigrationInspection.isLiteral(nextReturnStatement.getReturnValue(), !foundResult)) { - String methodName = foundResult ? "anyMatch" : "noneMatch"; - String streamText = generateStream(iteratedValue, operations).toString(); - streamText = addTerminalOperation(streamText, methodName, foreachStatement, tb); - boolean siblings = nextReturnStatement.getParent() == foreachStatement.getParent(); - PsiElement result = - foreachStatement.replace(elementFactory.createStatementFromText("return " + streamText + ";", foreachStatement)); - if (siblings) { - nextReturnStatement.delete(); + if(tb.getSingleStatement() instanceof PsiReturnStatement) { + PsiReturnStatement returnStatement = (PsiReturnStatement)tb.getSingleStatement(); + PsiExpression value = returnStatement.getReturnValue(); + if (StreamApiMigrationInspection.isLiteral(value, Boolean.TRUE) || StreamApiMigrationInspection.isLiteral(value, Boolean.FALSE)) { + boolean foundResult = (boolean)((PsiLiteralExpression)value).getValue(); + PsiReturnStatement nextReturnStatement = StreamApiMigrationInspection.getNextReturnStatement(foreachStatement); + if (nextReturnStatement != null && StreamApiMigrationInspection.isLiteral(nextReturnStatement.getReturnValue(), !foundResult)) { + String methodName = foundResult ? "anyMatch" : "noneMatch"; + String streamText = generateStream(iteratedValue, operations).toString(); + streamText = addTerminalOperation(streamText, methodName, foreachStatement, tb); + restoreComments(foreachStatement, body); + boolean siblings = nextReturnStatement.getParent() == foreachStatement.getParent(); + PsiElement result = + foreachStatement.replace(elementFactory.createStatementFromText("return " + streamText + ";", foreachStatement)); + if (siblings) { + nextReturnStatement.delete(); + } + simplifyAndFormat(project, result); + return; } - simplifyAndFormat(project, result); - return; } } - if (!StreamApiMigrationInspection.isVariableReferenced(tb.getVariable(), value)) { + PsiStatement[] statements = tb.getStatements(); + if(statements.length == 1 || (statements.length == 2 && statements[1] instanceof PsiBreakStatement)) { + restoreComments(foreachStatement, body); String streamText = generateStream(iteratedValue, operations).toString(); streamText = addTerminalOperation(streamText, "anyMatch", foreachStatement, tb); - String replacement = "if(" + streamText + "){" + returnStatement.getText() + "}"; + PsiStatement statement = statements[0]; + PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(statement); + if(assignment != null) { + PsiExpression lValue = assignment.getLExpression(); + PsiExpression rValue = assignment.getRExpression(); + if (!(lValue instanceof PsiReferenceExpression) || rValue == null) return; + PsiElement maybeVar = ((PsiReferenceExpression)lValue).resolve(); + if(maybeVar instanceof PsiVariable) { + // Simplify single assignments like this: + // boolean flag = false; + // for(....) if(...) {flag = true; break;} + PsiVariable var = (PsiVariable)maybeVar; + PsiExpression initializer = var.getInitializer(); + if(initializer != null && StreamApiMigrationInspection.isDeclarationJustBefore(var, foreachStatement)) { + String replacement; + if(StreamApiMigrationInspection.isLiteral(initializer, Boolean.FALSE) && + StreamApiMigrationInspection.isLiteral(rValue, Boolean.TRUE)) { + replacement = streamText; + } else if(StreamApiMigrationInspection.isLiteral(initializer, Boolean.TRUE) && + StreamApiMigrationInspection.isLiteral(rValue, Boolean.FALSE)) { + replacement = "!"+streamText; + } else { + replacement = streamText + "?" + rValue.getText() + ":" + initializer.getText(); + } + PsiElement result = initializer.replace(elementFactory.createExpressionFromText(replacement, initializer)); + removeLoop(foreachStatement); + simplifyAndFormat(project, result); + return; + } + } + } + String replacement = "if(" + streamText + "){" + statement.getText() + "}"; PsiElement result = foreachStatement.replace(elementFactory.createStatementFromText(replacement, foreachStatement)); simplifyAndFormat(project, result); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java index b53b5e1346fe..f9be7ffa1e4f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java @@ -516,14 +516,19 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo if (nonFinalVariables.isEmpty() && tb.getSingleStatement() instanceof PsiReturnStatement) { handleSingleReturn(statement, tb, operations); } - if (nonFinalVariables.size() == 1) { - PsiStatement[] statements = tb.getStatements(); - if (statements.length == 2) { - PsiStatement breakStatement = statements[1]; - if (!(breakStatement instanceof PsiBreakStatement) || - ((PsiBreakStatement)breakStatement).findExitedStatement() != statement) { - return; - } + PsiStatement[] statements = tb.getStatements(); + if (statements.length == 2) { + PsiStatement breakStatement = statements[1]; + if (!(breakStatement instanceof PsiBreakStatement) || + ((PsiBreakStatement)breakStatement).findExitedStatement() != statement) { + return; + } + if (ReferencesSearch.search(tb.getVariable(), new LocalSearchScope(statements)).findFirst() == null + && exitPoints.size() == 1 && exitPoints.contains(breakStatement)) { + registerProblem(statement, "anyMatch", new ReplaceWithMatchFix("anyMatch")); + return; + } + if (nonFinalVariables.size() == 1) { PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(statements[0]); if(assignment == null) return; PsiExpression lValue = assignment.getLExpression(); @@ -566,6 +571,9 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo } } if (!isVariableReferenced(tb.getVariable(), value)) { + if(!REPLACE_TRIVIAL_FOREACH && operations.isEmpty() || (operations.size() == 1 && operations.get(0) instanceof FilterOp)) { + return; + } registerProblem(statement, "anyMatch", new ReplaceWithMatchFix("anyMatch")); } if(nextReturnStatement != null && ExpressionUtils.isSimpleExpression(nextReturnStatement.getReturnValue()) diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchAssignment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchAssignment.java new file mode 100644 index 000000000000..cfdbf01b641b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchAssignment.java @@ -0,0 +1,9 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + public void testAssignment(List data) { + String found = data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty()) ? "yes" : "no"; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchAssignmentBoolean.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchAssignmentBoolean.java new file mode 100644 index 000000000000..fc0e5b198bc7 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchAssignmentBoolean.java @@ -0,0 +1,9 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + public void testAssignment(List data) { + boolean found = data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty()); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchAssignmentBooleanInverted.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchAssignmentBooleanInverted.java new file mode 100644 index 000000000000..24ba822d2458 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchAssignmentBooleanInverted.java @@ -0,0 +1,9 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + public void testAssignment(List data) { + boolean found = !data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty()); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchAssignmentNonTrivial.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchAssignmentNonTrivial.java new file mode 100644 index 000000000000..5d7e8f1d4dec --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchAssignmentNonTrivial.java @@ -0,0 +1,16 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + public void testAssignment(List data) { + boolean found = false; + if(Math.random() > 0.5) { + found = true; + } else { + if (data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty())) { + found = true; + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchMethodCall.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchMethodCall.java new file mode 100644 index 000000000000..db9ea29c79ce --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchMethodCall.java @@ -0,0 +1,11 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + public void testAssignment(List data) { + if (data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty())) { + System.out.println("Found!!!"); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstAssignmentCast.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstAssignmentCast.java new file mode 100644 index 000000000000..ce1ea6e97718 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstAssignmentCast.java @@ -0,0 +1,10 @@ +// "Replace with findFirst()" "true" + +import java.util.List; + +public class Main { + public void testCast(List data) { + String found = (String) data.stream().filter(obj -> obj instanceof String).findFirst().orElse(null); + System.out.println(found); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstCast.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstCast.java new file mode 100644 index 000000000000..ebc3c63ffc7a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstCast.java @@ -0,0 +1,9 @@ +// "Replace with findFirst()" "true" + +import java.util.List; + +public class Main { + public String testCast(List data) { + return (String) data.stream().filter(obj -> obj instanceof String).findFirst().orElse(null); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchAssignment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchAssignment.java new file mode 100644 index 000000000000..2f953ba99625 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchAssignment.java @@ -0,0 +1,16 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + public void testAssignment(List data) { + String found = "no"; + for(String str : data) { + String trimmed = str.trim(); + if(!trimmed.isEmpty()) { + found = "yes"; + break; + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchAssignmentBoolean.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchAssignmentBoolean.java new file mode 100644 index 000000000000..4d33ce319a01 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchAssignmentBoolean.java @@ -0,0 +1,16 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + public void testAssignment(List data) { + boolean found = false; + for(String str : data) { + String trimmed = str.trim(); + if(!trimmed.isEmpty()) { + found = true; + break; + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchAssignmentBooleanInverted.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchAssignmentBooleanInverted.java new file mode 100644 index 000000000000..7e52ef1502fd --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchAssignmentBooleanInverted.java @@ -0,0 +1,16 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + public void testAssignment(List data) { + boolean found = true; + for(String str : data) { + String trimmed = str.trim(); + if(!trimmed.isEmpty()) { + found = false; + break; + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchAssignmentNonTrivial.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchAssignmentNonTrivial.java new file mode 100644 index 000000000000..742d43ee19a6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchAssignmentNonTrivial.java @@ -0,0 +1,20 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + public void testAssignment(List data) { + boolean found = false; + if(Math.random() > 0.5) { + found = true; + } else { + for (String str : data) { + String trimmed = str.trim(); + if (!trimmed.isEmpty()) { + found = true; + break; + } + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchMethodCall.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchMethodCall.java new file mode 100644 index 000000000000..a59970f069f4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchMethodCall.java @@ -0,0 +1,15 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + public void testAssignment(List data) { + for (String str : data) { + String trimmed = str.trim(); + if (!trimmed.isEmpty()) { + System.out.println("Found!!!"); + break; + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstAssignmentCast.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstAssignmentCast.java new file mode 100644 index 000000000000..14d8f037cc99 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstAssignmentCast.java @@ -0,0 +1,16 @@ +// "Replace with findFirst()" "true" + +import java.util.List; + +public class Main { + public void testCast(List data) { + String found = null; + for (Object obj : data) { + if(obj instanceof String) { + found = (String)obj; + break; + } + } + System.out.println(found); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstCast.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstCast.java new file mode 100644 index 000000000000..7b24a6e3e45f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstCast.java @@ -0,0 +1,14 @@ +// "Replace with findFirst()" "true" + +import java.util.List; + +public class Main { + public String testCast(List data) { + for (Object obj : data) { + if(obj instanceof String) { + return (String)obj; + } + } + return null; + } +} \ No newline at end of file