Bug 424214 - Generation of equals and hashcode with java 7
Objects.equals and Objects.hashcode

Implemented Java 7 generator. Fixed Object[], Serializable[] and
Cloneable[] cases for all implementations.

Change-Id: I1b7258d69f3925b77b2f72f017afc64da7a0cac0
Signed-off-by: Pierre-Yves B. <pyvesdev@gmail.com>
Also-by: Ivan Konstantinov <threesixty@abv.bg>
diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/source/GenerateHashCodeEqualsTest.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/source/GenerateHashCodeEqualsTest.java
index d450dbe..670b0d6 100644
--- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/source/GenerateHashCodeEqualsTest.java
+++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/source/GenerateHashCodeEqualsTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2016 IBM Corporation and others.
+ * Copyright (c) 2000, 2018 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Pierre-Yves B. <pyvesdev@gmail.com> - Generation of equals and hashcode with java 7 Objects.equals and Objects.hashcode - https://bugs.eclipse.org/424214
  *******************************************************************************/
 package org.eclipse.jdt.ui.tests.core.source;
 
@@ -64,7 +65,7 @@
 		return setUpTest(new TestSuite(THIS));
 	}
 
-	public void runOperation(IType type, IField[] fields, IJavaElement insertBefore, boolean createComments, boolean useInstanceof, boolean useBlocks, boolean force) throws CoreException {
+	public void runOperation(IType type, IField[] fields, IJavaElement insertBefore, boolean createComments, boolean useInstanceof, boolean useJ7HashEquals, boolean useBlocks, boolean force) throws CoreException {
 
 		RefactoringASTParser parser= new RefactoringASTParser(IASTSharedValues.SHARED_AST_LEVEL);
 		CompilationUnit unit= parser.parse(type.getCompilationUnit(), true);
@@ -80,14 +81,18 @@
 
 		AbstractTypeDeclaration decl= ASTNodeSearchUtil.getAbstractTypeDeclarationNode(type, unit);
 		ITypeBinding binding= decl.resolveBinding();
-		GenerateHashCodeEqualsOperation op= new GenerateHashCodeEqualsOperation(binding, fKeys, unit, insertBefore, fSettings, useInstanceof, force, true, true);
+		GenerateHashCodeEqualsOperation op= new GenerateHashCodeEqualsOperation(binding, fKeys, unit, insertBefore, fSettings, useInstanceof, useJ7HashEquals, force, true, true);
 		op.setUseBlocksForThen(useBlocks);
 		op.run(new NullProgressMonitor());
 		JavaModelUtil.reconcile(type.getCompilationUnit());
 	}
 
 	public void runOperation(IType type, IField[] fields, boolean useInstanceof, boolean force) throws CoreException {
-		runOperation(type, fields, null, true, useInstanceof, false, force);
+		runOperation(type, fields, null, true, useInstanceof, false, false, force);
+	}
+
+	public void runJ7Operation(IType type, IField[] fields, boolean useInstanceof) throws CoreException {
+		runOperation(type, fields, null, true, useInstanceof, true, false, false);
 	}
 
 	private IField[] getFields(IType type, String[] fieldNames) {
@@ -493,7 +498,7 @@
 				"", true, null);
 
 		IField[] fields= getFields(a.getType("A"), new String[] {"someInt" });
-		runOperation(a.getType("A"), fields, a.getType("A").getMethod("bar", new String[0]), true, false, false, false);
+		runOperation(a.getType("A"), fields, a.getType("A").getMethod("bar", new String[0]), true, false, false, false, false);
 
 		String expected= "package p;\r\n" +
 				"\r\n" +
@@ -1056,6 +1061,775 @@
 	}
 
 	/**
+	 * Test with J7+ Objects.hash and Objects.equals method calls
+	 * Using non-array instance variables and Enum
+	 * 
+	 * @throws Exception
+	 */
+	public void testHashCodeEqualsIn17() throws Exception {
+
+		ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+				"import java.util.List;\r\n" +
+				"import java.lang.annotation.ElementType;\r\n" +
+				"public class A {\r\n" +
+				"	boolean aBool;\r\n" +
+				"	byte aByte;\r\n" +
+				"	char aChar;\r\n" +
+				"	int anInt;\r\n" +
+				"	double aDouble;\r\n" +
+				"	float aFloat;\r\n" +
+				"	long aLong;\r\n" +
+				"	String aString;\r\n" +
+				"	List<String> aListOfStrings;\r\n" +
+				"	ElementType anEnum;\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"", true, null);
+
+		IField[] fields= getFields(a.getType("A"), new String[] { "aBool", "aByte", "aChar", "anInt", "aDouble", "aFloat", "aLong", "aString", "aListOfStrings", "anEnum" });
+		runJ7Operation(a.getType("A"), fields, false);
+
+		String expected= "package p;\r\n" +
+				"import java.util.List;\r\n" +
+				"import java.util.Objects;\r\n" +
+				"import java.lang.annotation.ElementType;\r\n" +
+				"public class A {\r\n" +
+				"	boolean aBool;\r\n" +
+				"	byte aByte;\r\n" +
+				"	char aChar;\r\n" +
+				"	int anInt;\r\n" +
+				"	double aDouble;\r\n" +
+				"	float aFloat;\r\n" +
+				"	long aLong;\r\n" +
+				"	String aString;\r\n" +
+				"	List<String> aListOfStrings;\r\n" +
+				"	ElementType anEnum;\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#hashCode()\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public int hashCode() {\r\n" +
+				"		return Objects.hash(aBool, aByte, aChar, anInt, aDouble, aFloat, aLong, aString, aListOfStrings, anEnum);\r\n" +
+				"	}\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public boolean equals(Object obj) {\r\n" +
+				"		if (this == obj)\r\n" +
+				"			return true;\r\n" +
+				"		if (obj == null)\r\n" +
+				"			return false;\r\n" +
+				"		if (getClass() != obj.getClass())\r\n" +
+				"			return false;\r\n" +
+				"		A other = (A) obj;\r\n" +
+				"		return aBool == other.aBool && aByte == other.aByte && aChar == other.aChar && anInt == other.anInt && Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) && Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) && aLong == other.aLong && Objects.equals(aString, other.aString) && Objects.equals(aListOfStrings, other.aListOfStrings) && anEnum == other.anEnum;\r\n"
+				+
+				"	}\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"";
+
+		compareSource(expected, a.getSource());
+	}
+	
+	/**
+	 * Test with J7+ Objects.hash and Objects.equals method calls
+	 * Using unique non-array instance variables
+	 * 
+	 * @throws Exception
+	 */
+	public void testHashCodeEqualsUniqueFieldIn17() throws Exception {
+
+		ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+				"public class A {\r\n" +
+				"	String aString;\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"", true, null);
+
+		IField[] fields= getFields(a.getType("A"), new String[] { "aString" });
+		runJ7Operation(a.getType("A"), fields, false);
+
+		String expected= "package p;\r\n" +
+				"\r\n" +
+				"import java.util.Objects;\r\n" +
+				"\r\n" +
+				"public class A {\r\n" +
+				"	String aString;\r\n" +
+				"\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#hashCode()\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public int hashCode() {\r\n" +
+				"		return Objects.hash(aString);\r\n" +
+				"	}\r\n" +
+				"\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public boolean equals(Object obj) {\r\n" +
+				"		if (this == obj)\r\n" +
+				"			return true;\r\n" +
+				"		if (obj == null)\r\n" +
+				"			return false;\r\n" +
+				"		if (getClass() != obj.getClass())\r\n" +
+				"			return false;\r\n" +
+				"		A other = (A) obj;\r\n" +
+				"		return Objects.equals(aString, other.aString);\r\n" +
+				"	}\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"";
+
+		compareSource(expected, a.getSource());
+	}
+	
+	/**
+	 * Test with J7+ Objects.hash and Objects.equals method calls
+	 * Using non-array instance variables with 'instanceof' comparison
+	 * 
+	 * @throws Exception
+	 */
+	public void testHashCodeEqualsInstanceOfIn17() throws Exception {
+
+		ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+				"import java.util.List;\r\n" +
+				"public class A {\r\n" +
+				"	boolean aBool;\r\n" +
+				"	byte aByte;\r\n" +
+				"	char aChar;\r\n" +
+				"	int anInt;\r\n" +
+				"	double aDouble;\r\n" +
+				"	float aFloat;\r\n" +
+				"	long aLong;\r\n" +
+				"	String aString;\r\n" +
+				"	List<String> aListOfStrings;\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"", true, null);
+
+		IField[] fields= getFields(a.getType("A"), new String[] { "aBool", "aByte", "aChar", "anInt", "aDouble", "aFloat", "aLong", "aString", "aListOfStrings" });
+		runJ7Operation(a.getType("A"), fields, true);
+
+		String expected= "package p;\r\n" +
+				"import java.util.List;\r\n" +
+				"import java.util.Objects;\r\n" +
+				"public class A {\r\n" +
+				"	boolean aBool;\r\n" +
+				"	byte aByte;\r\n" +
+				"	char aChar;\r\n" +
+				"	int anInt;\r\n" +
+				"	double aDouble;\r\n" +
+				"	float aFloat;\r\n" +
+				"	long aLong;\r\n" +
+				"	String aString;\r\n" +
+				"	List<String> aListOfStrings;\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#hashCode()\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public int hashCode() {\r\n" +
+				"		return Objects.hash(aBool, aByte, aChar, anInt, aDouble, aFloat, aLong, aString, aListOfStrings);\r\n" +
+				"	}\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public boolean equals(Object obj) {\r\n" +
+				"		if (this == obj)\r\n" +
+				"			return true;\r\n" +
+				"		if (obj == null)\r\n" +
+				"			return false;\r\n" +
+				"		if (!(obj instanceof A))\r\n" +
+				"			return false;\r\n" +
+				"		A other = (A) obj;\r\n" +
+				"		return aBool == other.aBool && aByte == other.aByte && aChar == other.aChar && anInt == other.anInt && Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) && Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) && aLong == other.aLong && Objects.equals(aString, other.aString) && Objects.equals(aListOfStrings, other.aListOfStrings);\r\n"
+				+
+				"	}\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"";
+
+		compareSource(expected, a.getSource());
+	}
+	
+	/**
+	 * Test with J7+ Objects.hash and Objects.equals method calls
+	 * Using 1-dim array amongst other instance variables
+	 * 
+	 * @throws Exception
+	 */
+	public void testHashCodeEqualsArrayIn17() throws Exception {
+
+		ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+				"import java.util.List;\r\n" +
+				"public class A {\r\n" +
+				"	boolean aBool;\r\n" +
+				"	byte aByte;\r\n" +
+				"	char aChar;\r\n" +
+				"	int anInt;\r\n" +
+				"	double aDouble;\r\n" +
+				"	float aFloat;\r\n" +
+				"	long aLong;\r\n" +
+				"	String aString;\r\n" +
+				"	List<String> aListOfStrings;\r\n" +
+				"	int[] anArrayOfInts;\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"", true, null);
+
+		IField[] fields= getFields(a.getType("A"), new String[] { "aBool", "aByte", "aChar", "anInt", "aDouble", "aFloat", "aLong", "aString", "aListOfStrings", "anArrayOfInts" });
+		runJ7Operation(a.getType("A"), fields, false);
+
+		String expected= "package p;\r\n" +
+				"import java.util.Arrays;\r\n" +
+				"import java.util.List;\r\n" +
+				"import java.util.Objects;\r\n" +
+				"public class A {\r\n" +
+				"	boolean aBool;\r\n" +
+				"	byte aByte;\r\n" +
+				"	char aChar;\r\n" +
+				"	int anInt;\r\n" +
+				"	double aDouble;\r\n" +
+				"	float aFloat;\r\n" +
+				"	long aLong;\r\n" +
+				"	String aString;\r\n" +
+				"	List<String> aListOfStrings;\r\n" +
+				"	int[] anArrayOfInts;\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#hashCode()\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public int hashCode() {\r\n" +
+				"		final int prime = 31;\r\n" +
+				"		int result = 1;\r\n" +
+				"		result = prime * result + Objects.hash(aBool, aByte, aChar, anInt, aDouble, aFloat, aLong, aString, aListOfStrings);\r\n" +
+				"		result = prime * result + Arrays.hashCode(anArrayOfInts);\r\n" +
+				"		return result;\r\n" +
+				"	}\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public boolean equals(Object obj) {\r\n" +
+				"		if (this == obj)\r\n" +
+				"			return true;\r\n" +
+				"		if (obj == null)\r\n" +
+				"			return false;\r\n" +
+				"		if (getClass() != obj.getClass())\r\n" +
+				"			return false;\r\n" +
+				"		A other = (A) obj;\r\n" +
+				"		return aBool == other.aBool && aByte == other.aByte && aChar == other.aChar && anInt == other.anInt && Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) && Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) && aLong == other.aLong && Objects.equals(aString, other.aString) && Objects.equals(aListOfStrings, other.aListOfStrings) && Arrays.equals(anArrayOfInts, other.anArrayOfInts);\r\n"
+				+
+				"	}\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"";
+
+		compareSource(expected, a.getSource());
+	}
+
+	/**
+	 * Test with J7+ Objects.hash and Objects.equals method calls
+	 * Using 1-dim Cloneable array amongst other instance variables
+	 * @throws Exception
+	 */
+	public void testHashCodeEqualsCloneableArrayIn17() throws Exception {
+
+		ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+				"import java.util.List;\r\n" +
+				"public class A {\r\n" +
+				"	boolean aBool;\r\n" +
+				"	byte aByte;\r\n" +
+				"	char aChar;\r\n" +
+				"	int anInt;\r\n" +
+				"	double aDouble;\r\n" +
+				"	float aFloat;\r\n" +
+				"	long aLong;\r\n" +
+				"	String aString;\r\n" +
+				"	List<String> aListOfStrings;\r\n" +
+				"	Cloneable[] anArrayOfCloneables;\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"", true, null);
+
+		IField[] fields= getFields(a.getType("A"), new String[] { "aBool", "aByte", "aChar", "anInt", "aDouble", "aFloat", "aLong", "aString", "aListOfStrings", "anArrayOfCloneables" });
+		runJ7Operation(a.getType("A"), fields, false);
+
+		String expected= "package p;\r\n" +
+				"import java.util.Arrays;\r\n" +
+				"import java.util.List;\r\n" +
+				"import java.util.Objects;\r\n" +
+				"public class A {\r\n" +
+				"	boolean aBool;\r\n" +
+				"	byte aByte;\r\n" +
+				"	char aChar;\r\n" +
+				"	int anInt;\r\n" +
+				"	double aDouble;\r\n" +
+				"	float aFloat;\r\n" +
+				"	long aLong;\r\n" +
+				"	String aString;\r\n" +
+				"	List<String> aListOfStrings;\r\n" +
+				"	Cloneable[] anArrayOfCloneables;\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#hashCode()\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public int hashCode() {\r\n" +
+				"		final int prime = 31;\r\n" +
+				"		int result = 1;\r\n" +
+				"		result = prime * result + Objects.hash(aBool, aByte, aChar, anInt, aDouble, aFloat, aLong, aString, aListOfStrings);\r\n" +
+				"		result = prime * result + Arrays.deepHashCode(anArrayOfCloneables);\r\n" +
+				"		return result;\r\n" +
+				"	}\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public boolean equals(Object obj) {\r\n" +
+				"		if (this == obj)\r\n" +
+				"			return true;\r\n" +
+				"		if (obj == null)\r\n" +
+				"			return false;\r\n" +
+				"		if (getClass() != obj.getClass())\r\n" +
+				"			return false;\r\n" +
+				"		A other = (A) obj;\r\n" +
+				"		return aBool == other.aBool && aByte == other.aByte && aChar == other.aChar && anInt == other.anInt && Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) && Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) && aLong == other.aLong && Objects.equals(aString, other.aString) && Objects.equals(aListOfStrings, other.aListOfStrings) && Arrays.deepEquals(anArrayOfCloneables, other.anArrayOfCloneables);\r\n"
+				+
+				"	}\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"";
+
+		compareSource(expected, a.getSource());
+	}
+	
+	/**
+	 * Test with J7+ Objects.hash and Objects.equals method calls
+	 * Using 1-dim Serializable array amongst other instance variables
+	 * @throws Exception
+	 */
+	public void testHashCodeEqualsSerializableArrayIn17() throws Exception {
+
+		ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+				"import java.util.List;\r\n" +
+				"import java.io.Serializable;\r\n" +
+				"public class A {\r\n" +
+				"	boolean aBool;\r\n" +
+				"	byte aByte;\r\n" +
+				"	char aChar;\r\n" +
+				"	int anInt;\r\n" +
+				"	double aDouble;\r\n" +
+				"	float aFloat;\r\n" +
+				"	long aLong;\r\n" +
+				"	String aString;\r\n" +
+				"	List<String> aListOfStrings;\r\n" +
+				"	Serializable[] anArrayOfSerializables;\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"", true, null);
+
+		IField[] fields= getFields(a.getType("A"), new String[] { "aBool", "aByte", "aChar", "anInt", "aDouble", "aFloat", "aLong", "aString", "aListOfStrings", "anArrayOfSerializables" });
+		runJ7Operation(a.getType("A"), fields, false);
+
+		String expected= "package p;\r\n" +
+				"import java.util.Arrays;\r\n" +
+				"import java.util.List;\r\n" +
+				"import java.util.Objects;\r\n" +
+				"import java.io.Serializable;\r\n" +
+				"public class A {\r\n" +
+				"	boolean aBool;\r\n" +
+				"	byte aByte;\r\n" +
+				"	char aChar;\r\n" +
+				"	int anInt;\r\n" +
+				"	double aDouble;\r\n" +
+				"	float aFloat;\r\n" +
+				"	long aLong;\r\n" +
+				"	String aString;\r\n" +
+				"	List<String> aListOfStrings;\r\n" +
+				"	Serializable[] anArrayOfSerializables;\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#hashCode()\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public int hashCode() {\r\n" +
+				"		final int prime = 31;\r\n" +
+				"		int result = 1;\r\n" +
+				"		result = prime * result + Objects.hash(aBool, aByte, aChar, anInt, aDouble, aFloat, aLong, aString, aListOfStrings);\r\n" +
+				"		result = prime * result + Arrays.deepHashCode(anArrayOfSerializables);\r\n" +
+				"		return result;\r\n" +
+				"	}\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public boolean equals(Object obj) {\r\n" +
+				"		if (this == obj)\r\n" +
+				"			return true;\r\n" +
+				"		if (obj == null)\r\n" +
+				"			return false;\r\n" +
+				"		if (getClass() != obj.getClass())\r\n" +
+				"			return false;\r\n" +
+				"		A other = (A) obj;\r\n" +
+				"		return aBool == other.aBool && aByte == other.aByte && aChar == other.aChar && anInt == other.anInt && Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) && Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) && aLong == other.aLong && Objects.equals(aString, other.aString) && Objects.equals(aListOfStrings, other.aListOfStrings) && Arrays.deepEquals(anArrayOfSerializables, other.anArrayOfSerializables);\r\n"
+				+
+				"	}\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"";
+
+		compareSource(expected, a.getSource());
+	}
+	
+	/**
+	 * Test with J7+ Objects.hash and Objects.equals method calls
+	 * Using 1-dim Object array amongst other instance variables
+	 * @throws Exception
+	 */
+	public void testHashCodeEqualsObjectArrayIn17() throws Exception {
+
+		ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+				"import java.util.List;\r\n" +
+				"public class A {\r\n" +
+				"	boolean aBool;\r\n" +
+				"	byte aByte;\r\n" +
+				"	char aChar;\r\n" +
+				"	int anInt;\r\n" +
+				"	double aDouble;\r\n" +
+				"	float aFloat;\r\n" +
+				"	long aLong;\r\n" +
+				"	String aString;\r\n" +
+				"	List<String> aListOfStrings;\r\n" +
+				"	Object[] anArrayOfObjects;\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"", true, null);
+
+		IField[] fields= getFields(a.getType("A"), new String[] { "aBool", "aByte", "aChar", "anInt", "aDouble", "aFloat", "aLong", "aString", "aListOfStrings", "anArrayOfObjects" });
+		runJ7Operation(a.getType("A"), fields, false);
+
+		String expected= "package p;\r\n" +
+				"import java.util.Arrays;\r\n" +
+				"import java.util.List;\r\n" +
+				"import java.util.Objects;\r\n" +
+				"public class A {\r\n" +
+				"	boolean aBool;\r\n" +
+				"	byte aByte;\r\n" +
+				"	char aChar;\r\n" +
+				"	int anInt;\r\n" +
+				"	double aDouble;\r\n" +
+				"	float aFloat;\r\n" +
+				"	long aLong;\r\n" +
+				"	String aString;\r\n" +
+				"	List<String> aListOfStrings;\r\n" +
+				"	Object[] anArrayOfObjects;\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#hashCode()\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public int hashCode() {\r\n" +
+				"		final int prime = 31;\r\n" +
+				"		int result = 1;\r\n" +
+				"		result = prime * result + Objects.hash(aBool, aByte, aChar, anInt, aDouble, aFloat, aLong, aString, aListOfStrings);\r\n" +
+				"		result = prime * result + Arrays.deepHashCode(anArrayOfObjects);\r\n" +
+				"		return result;\r\n" +
+				"	}\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public boolean equals(Object obj) {\r\n" +
+				"		if (this == obj)\r\n" +
+				"			return true;\r\n" +
+				"		if (obj == null)\r\n" +
+				"			return false;\r\n" +
+				"		if (getClass() != obj.getClass())\r\n" +
+				"			return false;\r\n" +
+				"		A other = (A) obj;\r\n" +
+				"		return aBool == other.aBool && aByte == other.aByte && aChar == other.aChar && anInt == other.anInt && Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) && Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) && aLong == other.aLong && Objects.equals(aString, other.aString) && Objects.equals(aListOfStrings, other.aListOfStrings) && Arrays.deepEquals(anArrayOfObjects, other.anArrayOfObjects);\r\n"
+				+
+				"	}\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"";
+
+		compareSource(expected, a.getSource());
+	}
+	
+	/**
+	 * Test with J7+ Objects.hash and Objects.equals method calls
+	 * Using 1-dim type variable arrays extending Serializable and Number
+	 * @throws Exception
+	 */
+	public void testHashCodeEqualsTypeVariableArrayIn17() throws Exception {
+
+		ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+				"import java.io.Serializable;\r\n" +
+				"public class A <S extends Serializable, N extends Number> {\r\n" +
+				"	S[] anArrayOfS;\r\n" +
+				"	N[] anArrayOfN;\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"", true, null);
+
+		IField[] fields= getFields(a.getType("A"), new String[] { "anArrayOfS", "anArrayOfN" });
+		runJ7Operation(a.getType("A"), fields, false);
+
+		String expected= "package p;\r\n" +
+				"import java.io.Serializable;\r\n" +
+				"import java.util.Arrays;\r\n" +
+				"public class A <S extends Serializable, N extends Number> {\r\n" +
+				"	S[] anArrayOfS;\r\n" +
+				"	N[] anArrayOfN;\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#hashCode()\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public int hashCode() {\r\n" +
+				"		final int prime = 31;\r\n" +
+				"		int result = 1;\r\n" +
+				"		result = prime * result + Arrays.deepHashCode(anArrayOfS);\r\n" +
+				"		result = prime * result + Arrays.hashCode(anArrayOfN);\r\n" +
+				"		return result;\r\n" +
+				"	}\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public boolean equals(Object obj) {\r\n" +
+				"		if (this == obj)\r\n" +
+				"			return true;\r\n" +
+				"		if (obj == null)\r\n" +
+				"			return false;\r\n" +
+				"		if (getClass() != obj.getClass())\r\n" +
+				"			return false;\r\n" +
+				"		A other = (A) obj;\r\n" +
+				"		return Arrays.deepEquals(anArrayOfS, other.anArrayOfS) && Arrays.equals(anArrayOfN, other.anArrayOfN);\r\n"
+				+
+				"	}\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"";
+
+		compareSource(expected, a.getSource());
+	}
+	
+	/**
+	 * Test with J7+ Objects.hash and Objects.equals method calls
+	 * Using multidimensional array amongst other instance variables
+	 * @throws Exception
+	 */
+	public void testHashCodeEqualsMultiArrayIn17() throws Exception {
+
+		ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+				"import java.util.List;\r\n" +
+				"public class A {\r\n" +
+				"	boolean aBool;\r\n" +
+				"	byte aByte;\r\n" +
+				"	char aChar;\r\n" +
+				"	int anInt;\r\n" +
+				"	double aDouble;\r\n" +
+				"	float aFloat;\r\n" +
+				"	long aLong;\r\n" +
+				"	String aString;\r\n" +
+				"	List<String> aListOfStrings;\r\n" +
+				"	int[][] anArrayOfInts;\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"", true, null);
+
+		IField[] fields= getFields(a.getType("A"), new String[] { "aBool", "aByte", "aChar", "anInt", "aDouble", "aFloat", "aLong", "aString", "aListOfStrings", "anArrayOfInts" });
+		runJ7Operation(a.getType("A"), fields, false);
+
+		String expected= "package p;\r\n" +
+				"import java.util.Arrays;\r\n" +
+				"import java.util.List;\r\n" +
+				"import java.util.Objects;\r\n" +
+				"public class A {\r\n" +
+				"	boolean aBool;\r\n" +
+				"	byte aByte;\r\n" +
+				"	char aChar;\r\n" +
+				"	int anInt;\r\n" +
+				"	double aDouble;\r\n" +
+				"	float aFloat;\r\n" +
+				"	long aLong;\r\n" +
+				"	String aString;\r\n" +
+				"	List<String> aListOfStrings;\r\n" +
+				"	int[][] anArrayOfInts;\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#hashCode()\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public int hashCode() {\r\n" +
+				"		final int prime = 31;\r\n" +
+				"		int result = 1;\r\n" +
+				"		result = prime * result + Objects.hash(aBool, aByte, aChar, anInt, aDouble, aFloat, aLong, aString, aListOfStrings);\r\n" +
+				"		result = prime * result + Arrays.deepHashCode(anArrayOfInts);\r\n" +
+				"		return result;\r\n" +
+				"	}\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public boolean equals(Object obj) {\r\n" +
+				"		if (this == obj)\r\n" +
+				"			return true;\r\n" +
+				"		if (obj == null)\r\n" +
+				"			return false;\r\n" +
+				"		if (getClass() != obj.getClass())\r\n" +
+				"			return false;\r\n" +
+				"		A other = (A) obj;\r\n" +
+				"		return aBool == other.aBool && aByte == other.aByte && aChar == other.aChar && anInt == other.anInt && Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) && Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) && aLong == other.aLong && Objects.equals(aString, other.aString) && Objects.equals(aListOfStrings, other.aListOfStrings) && Arrays.deepEquals(anArrayOfInts, other.anArrayOfInts);\r\n"
+				+
+				"	}\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"";
+
+		compareSource(expected, a.getSource());
+	}
+	
+	/**
+	 * Test with J7+ Objects.hash and Objects.equals method calls
+	 * Using both multidimensional and 1-dimensional primitive arrays amongst other instance variables
+	 * 
+	 * @throws Exception
+	 */
+	public void testHashCodeEqualsVariousArraysIn17() throws Exception {
+
+		ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+				"import java.util.List;\r\n" +
+				"public class A {\r\n" +
+				"	boolean aBool;\r\n" +
+				"	byte aByte;\r\n" +
+				"	char aChar;\r\n" +
+				"	int anInt;\r\n" +
+				"	double aDouble;\r\n" +
+				"	float aFloat;\r\n" +
+				"	long aLong;\r\n" +
+				"	String aString;\r\n" +
+				"	List<String> aListOfStrings;\r\n" +
+				"	int[] anArrayOfInts;\r\n" +
+				"	String[][] anArrayOfStrings;\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"", true, null);
+
+		IField[] fields= getFields(a.getType("A"), new String[] { "aBool", "aByte", "aChar", "anInt", "aDouble", "aFloat", "aLong", "aString", "aListOfStrings", "anArrayOfInts", "anArrayOfStrings" });
+		runJ7Operation(a.getType("A"), fields, false);
+
+		String expected= "package p;\r\n" +
+				"import java.util.Arrays;\r\n" +
+				"import java.util.List;\r\n" +
+				"import java.util.Objects;\r\n" +
+				"public class A {\r\n" +
+				"	boolean aBool;\r\n" +
+				"	byte aByte;\r\n" +
+				"	char aChar;\r\n" +
+				"	int anInt;\r\n" +
+				"	double aDouble;\r\n" +
+				"	float aFloat;\r\n" +
+				"	long aLong;\r\n" +
+				"	String aString;\r\n" +
+				"	List<String> aListOfStrings;\r\n" +
+				"	int[] anArrayOfInts;\r\n" +
+				"	String[][] anArrayOfStrings;\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#hashCode()\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public int hashCode() {\r\n" +
+				"		final int prime = 31;\r\n" +
+				"		int result = 1;\r\n" +
+				"		result = prime * result + Objects.hash(aBool, aByte, aChar, anInt, aDouble, aFloat, aLong, aString, aListOfStrings);\r\n" +
+				"		result = prime * result + Arrays.hashCode(anArrayOfInts);\r\n" +
+				"		result = prime * result + Arrays.deepHashCode(anArrayOfStrings);\r\n" +
+				"		return result;\r\n" +
+				"	}\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public boolean equals(Object obj) {\r\n" +
+				"		if (this == obj)\r\n" +
+				"			return true;\r\n" +
+				"		if (obj == null)\r\n" +
+				"			return false;\r\n" +
+				"		if (getClass() != obj.getClass())\r\n" +
+				"			return false;\r\n" +
+				"		A other = (A) obj;\r\n" +
+				"		return aBool == other.aBool && aByte == other.aByte && aChar == other.aChar && anInt == other.anInt && Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) && Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) && aLong == other.aLong && Objects.equals(aString, other.aString) && Objects.equals(aListOfStrings, other.aListOfStrings) && Arrays.equals(anArrayOfInts, other.anArrayOfInts) && Arrays.deepEquals(anArrayOfStrings, other.anArrayOfStrings);\r\n"
+				+
+				"	}\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"";
+
+		compareSource(expected, a.getSource());
+	}
+	
+	/**
+	 * Test with J7+ Objects.hash and Objects.equals method calls
+	 * Using ONLY multidimensional and 1-dimensional arrays as instance variables
+	 * 
+	 * @throws Exception
+	 */
+	public void testHashCodeEqualsOnlyArraysIn17() throws Exception {
+
+		ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+				"public class A {\r\n" +
+				"	int[] anArrayOfInts;\r\n" +
+				"	String[][] anArrayOfStrings;\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"", true, null);
+
+		IField[] fields= getFields(a.getType("A"), new String[] { "anArrayOfInts", "anArrayOfStrings" });
+		runJ7Operation(a.getType("A"), fields, false);
+
+		String expected= "package p;\r\n" +
+				"\r\n" +
+				"import java.util.Arrays;\r\n" +
+				"\r\n" +
+				"public class A {\r\n" +
+				"	int[] anArrayOfInts;\r\n" +
+				"	String[][] anArrayOfStrings;\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#hashCode()\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public int hashCode() {\r\n" +
+				"		final int prime = 31;\r\n" +
+				"		int result = 1;\r\n" +
+				"		result = prime * result + Arrays.hashCode(anArrayOfInts);\r\n" +
+				"		result = prime * result + Arrays.deepHashCode(anArrayOfStrings);\r\n" +
+				"		return result;\r\n" +
+				"	}\r\n" +
+				"	/* (non-Javadoc)\r\n" +
+				"	 * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+				"	 */\r\n" +
+				"	@Override\r\n" +
+				"	public boolean equals(Object obj) {\r\n" +
+				"		if (this == obj)\r\n" +
+				"			return true;\r\n" +
+				"		if (obj == null)\r\n" +
+				"			return false;\r\n" +
+				"		if (getClass() != obj.getClass())\r\n" +
+				"			return false;\r\n" +
+				"		A other = (A) obj;\r\n" +
+				"		return Arrays.equals(anArrayOfInts, other.anArrayOfInts) && Arrays.deepEquals(anArrayOfStrings, other.anArrayOfStrings);\r\n" +
+				"	}\r\n" +
+				"\r\n" +
+				"}\r\n" +
+				"";
+
+		compareSource(expected, a.getSource());
+	}
+
+	/**
 	 * Test member types
 	 *
 	 * @throws Exception
@@ -1139,7 +1913,7 @@
 				"}", true, null);
 
 		IField[] fields= getFields(a.getType("A"), new String[] {"aBool", "obj" });
-		runOperation(a.getType("A"), fields, null, true, false, true, false);
+		runOperation(a.getType("A"), fields, null, true, false, false, true, false);
 
 		String expected= "package p;\r\n" +
 				"\r\n" +
@@ -1346,7 +2120,7 @@
 
 				IJavaElement insertBefore= i < NUM_MEMBERS ? children[i] : null;
 
-				runOperation(type, new IField[] { foo }, insertBefore, false, false, false, false);
+				runOperation(type, new IField[] { foo }, insertBefore, false, false, false, false, false);
 
 				IJavaElement[] newChildren= type.getChildren();
 				assertEquals(NUM_MEMBERS + 2, newChildren.length);
@@ -1381,7 +2155,7 @@
 				"", true, null);
 	
 		IField[] fields= getFields(a.getType("Sub"), new String[] {"name" });
-		runOperation(a.getType("Sub"), fields, null, false, false, false, false);
+		runOperation(a.getType("Sub"), fields, null, false, false, false, false, false);
 	
 		String expected= "package p;\r\n" +
 				"\r\n" +
diff --git a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/codemanipulation/GenerateHashCodeEqualsOperation.java b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/codemanipulation/GenerateHashCodeEqualsOperation.java
index b95772a..eb87f01 100644
--- a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/codemanipulation/GenerateHashCodeEqualsOperation.java
+++ b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/codemanipulation/GenerateHashCodeEqualsOperation.java
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Pierre-Yves B. <pyvesdev@gmail.com> - Generation of equals and hashcode with java 7 Objects.equals and Objects.hashcode - https://bugs.eclipse.org/424214
  *******************************************************************************/
 package org.eclipse.jdt.internal.corext.codemanipulation;
 
@@ -144,6 +145,8 @@
 
 	private static final String METHODNAME_HASH_CODE= "hashCode"; //$NON-NLS-1$
 
+	private static final String METHODNAME_HASH= "hash"; //$NON-NLS-1$
+
 	private static final String METHODNAME_DEEP_HASH_CODE= "deepHashCode"; //$NON-NLS-1$
 
 	private static final String METHODNAME_OUTER_TYPE= "getOuterType"; //$NON-NLS-1$
@@ -166,6 +169,14 @@
 
 	private static final String VARIABLE_NAME_INDEX= "index"; //$NON-NLS-1$
 
+	private static final String JAVA_UTIL_OBJECTS= "java.util.Objects"; //$NON-NLS-1$
+
+	private static final String TYPE_NAME_CLONEABLE= "Cloneable"; //$NON-NLS-1$
+
+	private static final String TYPE_NAME_SERIALIZABLE= "Serializable"; //$NON-NLS-1$
+
+	private static final String TYPE_NAME_OBJECT= "Object"; //$NON-NLS-1$
+
 	/** Should the resulting edit be applied? */
 	private final boolean fApply;
 
@@ -208,6 +219,12 @@
 	/** <code>true</code> to use 'instanceof' to compare types, <code>false</code> otherwise */
 	private final boolean fUseInstanceOf;
 
+	/**
+	 * <code>true</code> to use newer hashcode and equals method generation, using Java 7+
+	 * Objects.hash and Objects.equals, <code>false</code> to generate default methods
+	 */
+	private final boolean fUseJ7HashEquals;
+
 	/** <code>true</code> to use blocks for then */
 	private boolean fUseBlocksForThen;
 
@@ -223,6 +240,7 @@
 	 * @param insert the insertion point, or <code>null</code>
 	 * @param settings the code generation settings to use
 	 * @param useInstanceof <code>true</code> to use 'instanceof' to compare types, <code>false</code> otherwise
+	 * @param useJ7HashEquals <code>true</code> to use Java 7+ Objects.hash and Objects.equals methods, <code>false</code> otherwise
 	 * @param force <code>true</code> to force the regeneration of existing methods,
 	 *            <code>false</code> otherwise
 	 * @param apply <code>true</code> if the resulting edit should be applied,
@@ -231,7 +249,8 @@
 	 *            saved, <code>false</code> otherwise
 	 */
 	public GenerateHashCodeEqualsOperation(final ITypeBinding type, final IVariableBinding[] fields, final CompilationUnit unit,
-			final IJavaElement insert, final CodeGenerationSettings settings, final boolean useInstanceof, final boolean force, final boolean apply, final boolean save) {
+			final IJavaElement insert, final CodeGenerationSettings settings, final boolean useInstanceof, final boolean useJ7HashEquals, final boolean force, final boolean apply,
+			final boolean save) {
 		Assert.isNotNull(type);
 		Assert.isNotNull(fields);
 		Assert.isNotNull(unit);
@@ -244,6 +263,7 @@
 		fFields= fields;
 		fSettings= settings;
 		fUseInstanceOf= useInstanceof;
+		fUseJ7HashEquals= useJ7HashEquals;
 		fSave= save;
 		fApply= apply;
 		fDoubleCount= 0;
@@ -396,7 +416,7 @@
 
 		Block body= fAst.newBlock();
 		hashCodeMethod.setBody(body);
-
+	
 		// PRIME NUMBER
 		VariableDeclarationFragment frag= fAst.newVariableDeclarationFragment();
 		frag.setName(fAst.newSimpleName(VARIABLE_NAME_PRIME));
@@ -405,15 +425,16 @@
 		VariableDeclarationStatement primeNumberDeclaration= fAst.newVariableDeclarationStatement(frag);
 		primeNumberDeclaration.modifiers().add(fAst.newModifier(ModifierKeyword.FINAL_KEYWORD));
 		primeNumberDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT));
-		body.statements().add(primeNumberDeclaration);
+		if (!fUseJ7HashEquals)
+			body.statements().add(primeNumberDeclaration);
 
-		// RESULT
 		VariableDeclarationFragment fragment= fAst.newVariableDeclarationFragment();
 		fragment.setName(fAst.newSimpleName(VARIABLE_NAME_RESULT));
 
 		VariableDeclarationStatement resultDeclaration= fAst.newVariableDeclarationStatement(fragment);
 		resultDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT));
-		body.statements().add(resultDeclaration);
+		if (!fUseJ7HashEquals)
+			body.statements().add(resultDeclaration);
 
 		if (needsNoSuperCall(fType, METHODNAME_HASH_CODE, new ITypeBinding[0])) {
 			fragment.setInitializer(fAst.newNumberLiteral(INITIAL_HASHCODE_VALUE));
@@ -427,28 +448,56 @@
 			body.statements().add(createAddOuterHashCode());
 		}
 
-		for (int i= 0; i < fFields.length; i++) {
-			if (fFields[i].getType().isPrimitive()) {
-				Statement[] sts= createAddSimpleHashCode(fFields[i].getType(), new IHashCodeAccessProvider() {
+		ReturnStatement endReturn= fAst.newReturnStatement();
+		if (fUseJ7HashEquals) {
+			MethodInvocation j7Invoc= fAst.newMethodInvocation();
+			List<Statement> arrayStatements= new ArrayList<>();
 
-					@Override
-					public Expression getThisAccess(String name) {
-						return getThisAccessForHashCode(name);
+			for (int i= 0; i < fFields.length; i++) {
+				if (fFields[i].getType().isArray())
+					arrayStatements.add(createAddArrayHashCode(fFields[i]));
+				else
+					j7Invoc.arguments().add(fAst.newSimpleName(fFields[i].getName()));
+			}
+
+			if (!j7Invoc.arguments().isEmpty()) {
+				j7Invoc.setExpression(getQualifiedName(JAVA_UTIL_OBJECTS));
+				j7Invoc.setName(fAst.newSimpleName(METHODNAME_HASH));
+			}
+
+			if (arrayStatements.isEmpty()) {
+				endReturn.setExpression(j7Invoc);
+			} else {
+				body.statements().add(primeNumberDeclaration);
+				body.statements().add(resultDeclaration);
+				if (!j7Invoc.arguments().isEmpty())
+					body.statements().add(prepareAssignment(j7Invoc));
+				body.statements().addAll(arrayStatements);
+				endReturn.setExpression(fAst.newSimpleName(VARIABLE_NAME_RESULT));
+			}
+		} else {
+			for (int i= 0; i < fFields.length; i++) {
+				if (fFields[i].getType().isPrimitive()) {
+					Statement[] sts= createAddSimpleHashCode(fFields[i].getType(), new IHashCodeAccessProvider() {
+
+						@Override
+						public Expression getThisAccess(String name) {
+							return getThisAccessForHashCode(name);
+						}
+
+					}, fFields[i].getName(), false);
+					for (int j= 0; j < sts.length; j++) {
+						body.statements().add(sts[j]);
 					}
-
-				}, fFields[i].getName(), false);
-				for (int j= 0; j < sts.length; j++) {
-					body.statements().add(sts[j]);
-				}
-			} else if (fFields[i].getType().isArray())
-				body.statements().add(createAddArrayHashCode(fFields[i]));
-			else
-				body.statements().add(createAddQualifiedHashCode(fFields[i]));
+				} else if (fFields[i].getType().isArray())
+					body.statements().add(createAddArrayHashCode(fFields[i]));
+				else
+					body.statements().add(createAddQualifiedHashCode(fFields[i]));
+			}
+			endReturn.setExpression(fAst.newSimpleName(VARIABLE_NAME_RESULT));
 		}
 
 		// the last return:
-		ReturnStatement endReturn= fAst.newReturnStatement();
-		endReturn.setExpression(fAst.newSimpleName(VARIABLE_NAME_RESULT));
 		body.statements().add(endReturn);
 
 		// method comment
@@ -546,7 +595,7 @@
 	private Statement createAddArrayHashCode(IVariableBinding binding) {
 		MethodInvocation invoc= fAst.newMethodInvocation();
 		if (JavaModelUtil.is50OrHigher(fRewrite.getCu().getJavaProject())) {
-			if (binding.getType().getDimensions() > 1) {
+			if (needsDeepMethod(binding.getType())) {
 				invoc.setName(fAst.newSimpleName(METHODNAME_DEEP_HASH_CODE));
 			} else {
 				invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE));
@@ -860,33 +909,37 @@
 
 		body.statements().add(otherDeclaration);
 
-		if (isMemberType()) { // test outer type
-			body.statements().add(createOuterComparison());
+		if (fUseJ7HashEquals) {
+			body.statements().add(createJ7EqualsStatement());
+		} else {
+			if (isMemberType()) { // test outer type
+				body.statements().add(createOuterComparison());
+			}
+
+			for (int i= 0; i < fFields.length; i++) {
+				IVariableBinding field= fFields[i];
+				ITypeBinding type= field.getType();
+				if (type.isPrimitive() || type.isEnum())
+					body.statements().add(createSimpleComparison(field));
+				else if (type.isArray()) {
+					IJavaProject project= fUnit.getJavaElement().getJavaProject();
+					if (needsDeepMethod(type) && JavaModelUtil.is50OrHigher(project)) {
+						body.statements().add(createMultiArrayComparison(field.getName()));
+					} else {
+						body.statements().add(createArrayComparison(field.getName()));
+					}
+				} else
+					body.statements().add(createQualifiedComparison(field.getName()));
+
+			}
+
+			// the last return true:
+			ReturnStatement endReturn= fAst.newReturnStatement();
+			endReturn.setExpression(fAst.newBooleanLiteral(true));
+
+			body.statements().add(endReturn);
 		}
 
-		for (int i= 0; i < fFields.length; i++) {
-			IVariableBinding field= fFields[i];
-			ITypeBinding type= field.getType();
-			if (type.isPrimitive() || type.isEnum())
-				body.statements().add(createSimpleComparison(field));
-			else if (type.isArray()) {
-				IJavaProject project= fUnit.getJavaElement().getJavaProject();
-				if (type.getDimensions() > 1 && JavaModelUtil.is50OrHigher(project)) {
-					body.statements().add(createMultiArrayComparison(field.getName()));
-				} else {
-					body.statements().add(createArrayComparison(field.getName()));
-				}
-			} else
-				body.statements().add(createQualifiedComparison(field.getName()));
-
-		}
-
-		// the last return true:
-		ReturnStatement endReturn= fAst.newReturnStatement();
-		endReturn.setExpression(fAst.newBooleanLiteral(true));
-
-		body.statements().add(endReturn);
-
 		// method comment
 		if (fSettings != null) {
 			ITypeBinding object= fAst.resolveWellKnownType(JAVA_LANG_OBJECT);
@@ -903,6 +956,72 @@
 		return equalsMethodDeclaration;
 	}
 
+	private Statement createJ7EqualsStatement() {
+		// return Object.equals(anObjet, other.anObject) && ... ;
+		ReturnStatement returnStatement= fAst.newReturnStatement();
+
+		Expression equalsExp= createJ7EqualsExpression(fFields[0]);
+		if (fFields.length == 1) {
+			returnStatement.setExpression(equalsExp);
+		} else {
+			InfixExpression exp= fAst.newInfixExpression();
+			exp.setLeftOperand(equalsExp);
+			InfixExpression refExp= exp;
+			for (int i= 1; i < fFields.length; i++) {
+				equalsExp= createJ7EqualsExpression(fFields[i]);
+
+				refExp.setOperator(Operator.CONDITIONAL_AND);
+				if (i == fFields.length - 1) {
+					refExp.setRightOperand(equalsExp);
+				} else {
+					InfixExpression infixExp= fAst.newInfixExpression();
+					infixExp.setLeftOperand(equalsExp);
+					refExp.setRightOperand(infixExp);
+					refExp= infixExp;
+				}
+			}
+			returnStatement.setExpression(exp);
+		}
+		return returnStatement;
+	}
+
+	private Expression createJ7EqualsExpression(IVariableBinding variable) {
+		ITypeBinding type= variable.getType();
+		String name= variable.getName();
+		if (type.isPrimitive() || type.isEnum()) {
+			InfixExpression expression= fAst.newInfixExpression();
+			expression.setOperator(Operator.EQUALS);
+			if (isPrimitiveType(type, PrimitiveType.FLOAT)) {
+				// Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat)
+				expression.setLeftOperand(createFloatInvocation(getThisAccessForEquals(name)));
+				expression.setRightOperand(createFloatInvocation(getOtherAccess(name)));
+			} else if (isPrimitiveType(type, PrimitiveType.DOUBLE)) {
+				// Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble)
+				expression.setLeftOperand(createDoubleInvocation(getThisAccessForEquals(name)));
+				expression.setRightOperand(createDoubleInvocation(getOtherAccess(name)));
+			} else {
+				// anInt == other.anInt
+				expression.setLeftOperand(getThisAccessForEquals(name));
+				expression.setRightOperand(getOtherAccess(name));
+			}
+			return expression;
+		} else {
+			MethodInvocation invoc= fAst.newMethodInvocation();
+			if (type.isArray()) {
+				// Arrays.equals(anArray, other.anArray) or Arrays.deepEquals(anArray, other.anArray)
+				invoc.setExpression(getQualifiedName(JAVA_UTIL_ARRAYS));
+				invoc.setName(needsDeepMethod(type) ? fAst.newSimpleName(METHODNAME_DEEP_EQUALS) : fAst.newSimpleName(METHODNAME_EQUALS));
+			} else {
+				// Objects.equals(anObj, other.anObj)
+				invoc.setExpression(getQualifiedName(JAVA_UTIL_OBJECTS));
+				invoc.setName(fAst.newSimpleName(METHODNAME_EQUALS));
+			}
+			invoc.arguments().add(fAst.newSimpleName(name));
+			invoc.arguments().add(getOtherAccess(name));
+			return invoc;
+		}
+	}
+
 	private Statement createOuterComparison() {
 		MethodInvocation outer1= fAst.newMethodInvocation();
 		outer1.setName(fAst.newSimpleName(METHODNAME_OUTER_TYPE));
@@ -1158,4 +1277,9 @@
 		return ( (name.equals(VARIABLE_NAME_EQUALS_CASTED)) || (name.equals(VARIABLE_NAME_EQUALS_PARAM)));
 	}
 
+	private boolean needsDeepMethod(ITypeBinding type) {
+		String elementTypeName= type.getErasure().getElementType().getName();
+		return type.getDimensions() > 1 || TYPE_NAME_CLONEABLE.equals(elementTypeName) || TYPE_NAME_SERIALIZABLE.equals(elementTypeName) || TYPE_NAME_OBJECT.equals(elementTypeName);
+	}
+
 }
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaUIMessages.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaUIMessages.java
index 09acc3f..be4de14 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaUIMessages.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaUIMessages.java
@@ -26,6 +26,7 @@
 
 	public static String FilteredTypesSelectionDialog_TypeFiltersPreferencesAction_label;
 	public static String GenerateHashCodeEqualsDialog_blocks_button;
+	public static String GenerateHashCodeEqualsDialog_j7hashequals_button;
 	public static String GenerateHashCodeEqualsDialog_instanceof_button;
 	public static String JavaPlugin_additionalInfo_affordance;
 	public static String JavaPlugin_internal_error;
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaUIMessages.properties b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaUIMessages.properties
index 9220ae1..2b425df 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaUIMessages.properties
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaUIMessages.properties
@@ -1,5 +1,5 @@
 ###############################################################################
-# Copyright (c) 2000, 2017 IBM Corporation and others.
+# Copyright (c) 2000, 2018 IBM Corporation and others.
 # All rights reserved. This program and the accompanying materials
 # are made available under the terms of the Eclipse Public License v1.0
 # which accompanies this distribution, and is available at
@@ -11,6 +11,7 @@
 #     Mateusz Matela <mateusz.matela@gmail.com> - [toString] Template edit dialog has usability issues - https://bugs.eclipse.org/bugs/show_bug.cgi?id=267916
 #     Mateusz Matela <mateusz.matela@gmail.com> - [toString] finish toString() builder wizard - https://bugs.eclipse.org/bugs/show_bug.cgi?id=267710
 #     Mateusz Matela <mateusz.matela@gmail.com> - [toString] toString() generator: Fields in declaration order - https://bugs.eclipse.org/bugs/show_bug.cgi?id=279924
+#     Pierre-Yves B. <pyvesdev@gmail.com> - Generation of equals and hashcode with java 7 Objects.equals and Objects.hashcode - https://bugs.eclipse.org/bugs/show_bug.cgi?id=424214
 ###############################################################################
 
 JavaPlugin_additionalInfo_affordance=Press 'Tab' from proposal table or click for focus
@@ -71,6 +72,7 @@
 ProblemMarkerManager_problem_marker_update_job_description=Sending problem marker updates...
 
 GenerateHashCodeEqualsDialog_blocks_button=Use &blocks in 'if' statements
+GenerateHashCodeEqualsDialog_j7hashequals_button=Use Objects.hash and Objects.equals methods (1.7 or higher)
 GenerateHashCodeEqualsDialog_dialog_title=Generate hashCode() and equals()
 GenerateHashCodeEqualsDialog_selectioninfo_more={0} of {1} selected.
 GenerateHashCodeEqualsDialog_instanceof_button=&Use 'instanceof' to compare types
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/dialogs/GenerateHashCodeEqualsDialog.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/dialogs/GenerateHashCodeEqualsDialog.java
index 38c9a17..36e22c1 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/dialogs/GenerateHashCodeEqualsDialog.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/dialogs/GenerateHashCodeEqualsDialog.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2017 IBM Corporation and others.
+ * Copyright (c) 2005, 2018 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Pierre-Yves B. <pyvesdev@gmail.com> - Generation of equals and hashcode with java 7 Objects.equals and Objects.hashcode - https://bugs.eclipse.org/424214
  *******************************************************************************/
 package org.eclipse.jdt.internal.ui.dialogs;
 
@@ -27,10 +28,12 @@
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.dialogs.ISelectionStatusValidator;
 
+import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.IType;
 import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.jdt.core.dom.IVariableBinding;
 
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
 import org.eclipse.jdt.internal.corext.util.Messages;
 
 import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
@@ -119,12 +122,16 @@
 
 	private static final String SETTINGS_INSTANCEOF= "InstanceOf"; //$NON-NLS-1$
 	private static final String SETTINGS_BLOCKS= "Blocks"; //$NON-NLS-1$
+	private static final String SETTINGS_J7_HASH_EQUALS= "Objects.equals & Objects.hash"; //$NON-NLS-1$
 
 	private boolean fUseInstanceOf;
 	private boolean fUseBlocks;
+	private boolean fUseJ7HashEquals;
+	private IJavaProject fProject;
 
 	public GenerateHashCodeEqualsDialog(Shell shell, CompilationUnitEditor editor, IType type, IVariableBinding[] allFields, IVariableBinding[] selectedFields) throws JavaModelException {
 		super(shell, new BindingLabelProvider(), new GenerateHashCodeEqualsContentProvider(allFields), editor, type, false);
+		this.fProject = type.getJavaProject();
 		setEmptyListMessage(JavaUIMessages.GenerateHashCodeEqualsDialog_no_entries);
 
 		setInitialSelections((Object[]) selectedFields);
@@ -137,12 +144,14 @@
 
 		fUseInstanceOf= asBoolean(getDialogSettings().get(SETTINGS_INSTANCEOF), false);
 		fUseBlocks= asBoolean(getDialogSettings().get(SETTINGS_BLOCKS), false);
+		fUseJ7HashEquals= asBoolean(getDialogSettings().get(SETTINGS_J7_HASH_EQUALS), false);
 	}
 
 	@Override
 	public boolean close() {
 		getDialogSettings().put(SETTINGS_INSTANCEOF, fUseInstanceOf);
 		getDialogSettings().put(SETTINGS_BLOCKS, fUseBlocks);
+		getDialogSettings().put(SETTINGS_J7_HASH_EQUALS, fUseJ7HashEquals);
 		return super.close();
 	}
 
@@ -182,6 +191,23 @@
 			}
 		});
 		button.setSelection(isUseBlocks());
+
+		button= new Button(composite, SWT.CHECK);
+		button.setText(JavaUIMessages.GenerateHashCodeEqualsDialog_j7hashequals_button);
+		if (JavaModelUtil.is17OrHigher(this.fProject)) {
+			button.addSelectionListener(new SelectionAdapter() {
+
+				@Override
+				public void widgetSelected(SelectionEvent event) {
+					setUseJ7HashEquals((((Button) event.widget).getSelection()));
+				}
+			});
+			button.setSelection(isUseJ7HashEquals());
+		} else {
+			button.setEnabled(false);
+			button.setSelection(false);
+			setUseJ7HashEquals(false);
+		}
 		data= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
 		data.horizontalSpan= 2;
 		button.setLayoutData(data);
@@ -205,6 +231,14 @@
 		fUseBlocks= useBlocks;
 	}
 
+	public boolean isUseJ7HashEquals() {
+		return fUseJ7HashEquals;
+	}
+
+	public void setUseJ7HashEquals(boolean useJ7HashEquals) {
+		fUseJ7HashEquals= useJ7HashEquals;
+	}
+
 	@Override
 	protected void createButtonsForButtonBar(Composite parent) {
 		createButton(parent, IDialogConstants.OK_ID, JavaUIMessages.GenerateHashCodeEqualsDialog_generate, true);
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/actions/GenerateHashCodeEqualsAction.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/actions/GenerateHashCodeEqualsAction.java
index 340058a..da64766 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/actions/GenerateHashCodeEqualsAction.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/actions/GenerateHashCodeEqualsAction.java
@@ -85,6 +85,7 @@
 	private class HashCodeEqualsGenerationSettings extends CodeGenerationSettings {
 		public boolean useInstanceOf= false;
 		public boolean useBlocks= false;
+		public boolean useJ7HashEquals= false;
 	}
 
 	private List<IVariableBinding> allFields;
@@ -209,6 +210,7 @@
 		GenerateHashCodeEqualsDialog generateHashCodeEqualsDialog= (GenerateHashCodeEqualsDialog)dialog;
 		settings.useInstanceOf= generateHashCodeEqualsDialog.isUseInstanceOf();
 		settings.useBlocks= generateHashCodeEqualsDialog.isUseBlocks();
+		settings.useJ7HashEquals= generateHashCodeEqualsDialog.isUseJ7HashEquals();
 		return settings;
 	}
 
@@ -281,7 +283,7 @@
 		final IVariableBinding[] selectedVariableBindings= Arrays.asList(selectedBindings).toArray(new IVariableBinding[0]);
 		HashCodeEqualsGenerationSettings hashCodeEqualsGenerationSettings= (HashCodeEqualsGenerationSettings)settings;
 		GenerateHashCodeEqualsOperation operation= new GenerateHashCodeEqualsOperation(fTypeBinding, selectedVariableBindings, fUnit, elementPosition, settings,
-				hashCodeEqualsGenerationSettings.useInstanceOf, regenerate, true, false);
+				hashCodeEqualsGenerationSettings.useInstanceOf, hashCodeEqualsGenerationSettings.useJ7HashEquals, regenerate, true, false);
 		operation.setUseBlocksForThen(hashCodeEqualsGenerationSettings.useBlocks);
 		return operation;
 	}